mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-13 04:45:19 +00:00
load demo example
This commit is contained in:
parent
1114ee57f5
commit
463477c85e
5 changed files with 69 additions and 106 deletions
|
|
@ -12,6 +12,7 @@ export type {
|
|||
export {
|
||||
base64Decode,
|
||||
loadConfig,
|
||||
loadJsonFile,
|
||||
isRemotePath,
|
||||
} from "./utils.js";
|
||||
export {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { indexSchema } from "@sourcebot/schemas/v3/index.schema";
|
|||
import { readFile } from 'fs/promises';
|
||||
import stripJsonComments from 'strip-json-comments';
|
||||
import { Ajv } from "ajv";
|
||||
import { z } from "zod";
|
||||
|
||||
const ajv = new Ajv({
|
||||
validateFormats: false,
|
||||
|
|
@ -18,6 +19,66 @@ export const isRemotePath = (path: string) => {
|
|||
return path.startsWith('https://') || path.startsWith('http://');
|
||||
}
|
||||
|
||||
// TODO: Merge this with config loading logic which uses AJV
|
||||
export const loadJsonFile = async <T>(
|
||||
filePath: string,
|
||||
schema: any
|
||||
): Promise<T> => {
|
||||
const fileContent = await (async () => {
|
||||
if (isRemotePath(filePath)) {
|
||||
const response = await fetch(filePath);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch file ${filePath}: ${response.statusText}`);
|
||||
}
|
||||
return response.text();
|
||||
} else {
|
||||
// Retry logic for handling race conditions with mounted volumes
|
||||
const maxAttempts = 5;
|
||||
const retryDelayMs = 2000;
|
||||
let lastError: Error | null = null;
|
||||
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||
try {
|
||||
return await readFile(filePath, {
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
} catch (error) {
|
||||
lastError = error as Error;
|
||||
|
||||
// Only retry on ENOENT errors (file not found)
|
||||
if ((error as NodeJS.ErrnoException)?.code !== 'ENOENT') {
|
||||
throw error; // Throw immediately for non-ENOENT errors
|
||||
}
|
||||
|
||||
// Log warning before retry (except on the last attempt)
|
||||
if (attempt < maxAttempts) {
|
||||
console.warn(`File not found, retrying in 2s... (Attempt ${attempt}/${maxAttempts})`);
|
||||
await new Promise(resolve => setTimeout(resolve, retryDelayMs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we've exhausted all retries, throw the last ENOENT error
|
||||
if (lastError) {
|
||||
throw lastError;
|
||||
}
|
||||
|
||||
throw new Error('Failed to load file after all retry attempts');
|
||||
}
|
||||
})();
|
||||
|
||||
const parsedData = JSON.parse(stripJsonComments(fileContent));
|
||||
|
||||
try {
|
||||
return schema.parse(parsedData);
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
throw new Error(`File '${filePath}' is invalid: ${error.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', ')}`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export const loadConfig = async (configPath: string): Promise<SourcebotConfig> => {
|
||||
const configContent = await (async () => {
|
||||
if (isRemotePath(configPath)) {
|
||||
|
|
|
|||
|
|
@ -5,11 +5,8 @@ import { ChatBox } from "@/features/chat/components/chatBox";
|
|||
import { ChatBoxToolbar } from "@/features/chat/components/chatBox/chatBoxToolbar";
|
||||
import { LanguageModelInfo } from "@/features/chat/types";
|
||||
import { useCreateNewChatThread } from "@/features/chat/useCreateNewChatThread";
|
||||
import { resetEditor } from "@/features/chat/utils";
|
||||
import { useDomain } from "@/hooks/useDomain";
|
||||
import { RepositoryQuery, SearchContextQuery } from "@/lib/types";
|
||||
import { getDisplayTime } from "@/lib/utils";
|
||||
import { Code, Database, FileIcon, FileText, Gamepad2, Globe, Layers, LucideIcon, Search, SearchIcon, Smartphone, Zap } from "lucide-react";
|
||||
import { Layers, Search } from "lucide-react";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { useState } from "react";
|
||||
import { SearchModeSelector, SearchModeSelectorProps } from "./toolbar";
|
||||
|
|
@ -19,14 +16,6 @@ import { useLocalStorage } from "usehooks-ts";
|
|||
import { ContextItem } from "@/features/chat/components/chatBox/contextSelector";
|
||||
import { DemoExamples, DemoSearchExample, DemoSearchContextExample } from "@/types";
|
||||
|
||||
const Highlight = ({ children }: { children: React.ReactNode }) => {
|
||||
return (
|
||||
<span className="text-highlight">
|
||||
{children}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
interface AgenticSearchProps {
|
||||
searchModeSelectorProps: SearchModeSelectorProps;
|
||||
languageModels: LanguageModelInfo[];
|
||||
|
|
@ -40,101 +29,11 @@ interface AgenticSearchProps {
|
|||
demoExamples: DemoExamples | undefined;
|
||||
}
|
||||
|
||||
const exampleSearches = [
|
||||
{
|
||||
id: "1",
|
||||
title: "Show me examples of how useMemo is used",
|
||||
description: "Find React performance optimization patterns",
|
||||
icon: <Zap className="h-4 w-4" />,
|
||||
category: "React",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
title: "How do I implement authentication?",
|
||||
description: "Explore auth patterns and best practices",
|
||||
icon: <Database className="h-4 w-4" />,
|
||||
category: "Security",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
title: "Find API route handlers",
|
||||
description: "Locate and analyze API endpoint implementations",
|
||||
icon: <Globe className="h-4 w-4" />,
|
||||
category: "Backend",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
title: "Show me error handling patterns",
|
||||
description: "Discover error boundary and exception handling",
|
||||
icon: <FileText className="h-4 w-4" />,
|
||||
category: "Best Practices",
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
title: "How are components structured?",
|
||||
description: "Analyze component architecture and patterns",
|
||||
icon: <Layers className="h-4 w-4" />,
|
||||
category: "Architecture",
|
||||
},
|
||||
]
|
||||
|
||||
const searchContextsExample = [
|
||||
{
|
||||
id: "1",
|
||||
displayName: "Next.js",
|
||||
name: "nextjs",
|
||||
description: "React framework for production",
|
||||
icon: <Code className="h-5 w-5" />,
|
||||
color: "bg-black text-white",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
displayName: "React",
|
||||
name: "react",
|
||||
description: "JavaScript library for building UIs",
|
||||
icon: <Code className="h-5 w-5" />,
|
||||
color: "bg-blue-500 text-white",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
displayName: "TypeScript",
|
||||
name: "typescript",
|
||||
description: "Typed JavaScript at scale",
|
||||
icon: <FileText className="h-5 w-5" />,
|
||||
color: "bg-blue-600 text-white",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
displayName: "Tailwind CSS",
|
||||
name: "tailwindcss",
|
||||
description: "Utility-first CSS framework",
|
||||
icon: <Layers className="h-5 w-5" />,
|
||||
color: "bg-cyan-500 text-white",
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
displayName: "Godot Engine",
|
||||
name: "godot",
|
||||
description: "Open source game engine",
|
||||
icon: <Gamepad2 className="h-5 w-5" />,
|
||||
color: "bg-blue-400 text-white",
|
||||
},
|
||||
{
|
||||
id: "6",
|
||||
displayName: "React Native",
|
||||
name: "react-native",
|
||||
description: "Build mobile apps with React",
|
||||
icon: <Smartphone className="h-5 w-5" />,
|
||||
color: "bg-purple-500 text-white",
|
||||
},
|
||||
]
|
||||
|
||||
export const AgenticSearch = ({
|
||||
searchModeSelectorProps,
|
||||
languageModels,
|
||||
repos,
|
||||
searchContexts,
|
||||
chatHistory,
|
||||
demoExamples,
|
||||
}: AgenticSearchProps) => {
|
||||
const { createNewChatThread, isLoading } = useCreateNewChatThread();
|
||||
|
|
@ -226,7 +125,7 @@ export const AgenticSearch = ({
|
|||
<h3 className="text-lg font-semibold">Search Contexts</h3>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-1 xl:grid-cols-2 gap-3">
|
||||
{demoExamples.searchContexts?.map((context) => {
|
||||
{demoExamples.searchContextExamples.map((context) => {
|
||||
const searchContext = searchContexts.find((item) => item.name === context.name);
|
||||
if (!searchContext) return null;
|
||||
const isSelected = false; //selectedItems.some((item) => item.id === context.id)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { getRepos, getSearchContexts } from "@/actions";
|
|||
import { Footer } from "@/app/components/footer";
|
||||
import { getOrgFromDomain } from "@/data/org";
|
||||
import { getConfiguredLanguageModelsInfo, getUserChatHistory } from "@/features/chat/actions";
|
||||
import { isServiceError, loadDemoExamples } from "@/lib/utils";
|
||||
import { isServiceError } from "@/lib/utils";
|
||||
import { Homepage } from "./components/homepage";
|
||||
import { NavigationMenu } from "./components/navigationMenu";
|
||||
import { PageNotFound } from "./components/pageNotFound";
|
||||
|
|
@ -12,6 +12,8 @@ import { auth } from "@/auth";
|
|||
import { cookies } from "next/headers";
|
||||
import { SEARCH_MODE_COOKIE_NAME } from "@/lib/constants";
|
||||
import { env } from "@/env.mjs";
|
||||
import { loadJsonFile } from "@sourcebot/shared";
|
||||
import { DemoExamples, demoExamplesSchema } from "@/types";
|
||||
|
||||
export default async function Home({ params: { domain } }: { params: { domain: string } }) {
|
||||
const org = await getOrgFromDomain(domain);
|
||||
|
|
@ -49,7 +51,7 @@ export default async function Home({ params: { domain } }: { params: { domain: s
|
|||
searchModeCookie?.value === "precise"
|
||||
) ? searchModeCookie.value : models.length > 0 ? "agentic" : "precise";
|
||||
|
||||
const demoExamples = undefined; //await loadDemoExamples(env.SOURCEBOT_DEMO_EXAMPLES_PATH);
|
||||
const demoExamples = env.SOURCEBOT_DEMO_EXAMPLES_PATH ? await loadJsonFile<DemoExamples>(env.SOURCEBOT_DEMO_EXAMPLES_PATH, demoExamplesSchema) : undefined;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center overflow-hidden min-h-screen">
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export const demoSearchContextExampleSchema = z.object({
|
|||
|
||||
export const demoExamplesSchema = z.object({
|
||||
searchExamples: demoSearchExampleSchema.array(),
|
||||
searchContexts: demoSearchContextExampleSchema.array(),
|
||||
searchContextExamples: demoSearchContextExampleSchema.array(),
|
||||
})
|
||||
|
||||
export type OrgMetadata = z.infer<typeof orgMetadataSchema>;
|
||||
|
|
|
|||
Loading…
Reference in a new issue