first commit
This commit is contained in:
323
ui/app/workspace/logs/views/emptyState.tsx
Normal file
323
ui/app/workspace/logs/views/emptyState.tsx
Normal file
@@ -0,0 +1,323 @@
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { CodeEditor } from "@/components/ui/codeEditor";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { useCopyToClipboard } from "@/hooks/useCopyToClipboard";
|
||||
import { getExampleBaseUrl } from "@/lib/utils/port";
|
||||
import { AlertTriangle, Copy } from "lucide-react";
|
||||
import { useMemo, useState } from "react";
|
||||
|
||||
type Provider = "openai" | "anthropic" | "genai" | "litellm" | "langchain";
|
||||
type Language = "python" | "typescript";
|
||||
|
||||
type Examples = {
|
||||
curl: string;
|
||||
sdk: {
|
||||
[P in Provider]: {
|
||||
[L in Language]: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// Common editor options to reduce duplication
|
||||
const EditorOptions = {
|
||||
scrollBeyondLastLine: false,
|
||||
minimap: { enabled: false },
|
||||
lineNumbers: "off",
|
||||
folding: false,
|
||||
lineDecorationsWidth: 0,
|
||||
lineNumbersMinChars: 0,
|
||||
glyphMargin: false,
|
||||
} as const;
|
||||
|
||||
interface CodeBlockProps {
|
||||
code: string;
|
||||
language: string;
|
||||
onLanguageChange?: (language: string) => void;
|
||||
showLanguageSelect?: boolean;
|
||||
readonly?: boolean;
|
||||
}
|
||||
|
||||
function CodeBlock({ code, language, onLanguageChange, showLanguageSelect = false, readonly = true }: CodeBlockProps) {
|
||||
const { copy: copyToClipboard } = useCopyToClipboard();
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<div className="absolute top-4 right-4 z-10 flex items-center gap-2">
|
||||
{showLanguageSelect && onLanguageChange && (
|
||||
<Select value={language} onValueChange={onLanguageChange}>
|
||||
<SelectTrigger className="h-8 w-fit text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem className="text-xs" value="python">
|
||||
Python
|
||||
</SelectItem>
|
||||
<SelectItem className="text-xs" value="typescript">
|
||||
TypeScript
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
<Button variant="ghost" size="icon" onClick={() => copyToClipboard(code)}>
|
||||
<Copy className="size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<CodeEditor className="w-full" code={code} lang={language} readonly={readonly} height={300} fontSize={14} options={EditorOptions} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface EmptyStateProps {
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
export function EmptyState({ error }: EmptyStateProps) {
|
||||
const [language, setLanguage] = useState<Language>("python");
|
||||
|
||||
// Generate examples dynamically using the port utility
|
||||
const examples: Examples = useMemo(() => {
|
||||
const baseUrl = getExampleBaseUrl();
|
||||
|
||||
return {
|
||||
curl: `curl -X POST ${baseUrl}/v1/chat/completions \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"model": "openai/gpt-4o-mini",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello!"}
|
||||
]
|
||||
}'`,
|
||||
sdk: {
|
||||
openai: {
|
||||
python: `import openai
|
||||
|
||||
client = openai.OpenAI(
|
||||
base_url="${baseUrl}/openai",
|
||||
api_key="dummy-api-key" # Handled by Bifrost
|
||||
)
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o-mini", # or "provider/model" for other providers (anthropic/claude-3-sonnet)
|
||||
messages=[{"role": "user", "content": "Hello!"}]
|
||||
)`,
|
||||
typescript: `import OpenAI from "openai";
|
||||
|
||||
const openai = new OpenAI({
|
||||
baseURL: "${baseUrl}/openai",
|
||||
apiKey: "dummy-api-key", // Handled by Bifrost
|
||||
});
|
||||
|
||||
const response = await openai.chat.completions.create({
|
||||
model: "gpt-4o-mini", // or "provider/model" for other providers (anthropic/claude-3-sonnet)
|
||||
messages: [{ role: "user", content: "Hello!" }],
|
||||
});`,
|
||||
},
|
||||
anthropic: {
|
||||
python: `import anthropic
|
||||
|
||||
client = anthropic.Anthropic(
|
||||
base_url="${baseUrl}/anthropic",
|
||||
api_key="dummy-api-key" # Handled by Bifrost
|
||||
)
|
||||
|
||||
response = client.messages.create(
|
||||
model="claude-3-sonnet-20240229", # or "provider/model" for other providers (openai/gpt-4o-mini)
|
||||
max_tokens=1000,
|
||||
messages=[{"role": "user", "content": "Hello!"}]
|
||||
)`,
|
||||
typescript: `import Anthropic from "@anthropic-ai/sdk";
|
||||
|
||||
const anthropic = new Anthropic({
|
||||
baseURL: "${baseUrl}/anthropic",
|
||||
apiKey: "dummy-api-key", // Handled by Bifrost
|
||||
});
|
||||
|
||||
const response = await anthropic.messages.create({
|
||||
model: "claude-3-sonnet-20240229", // or "provider/model" for other providers (openai/gpt-4o-mini)
|
||||
max_tokens: 1000,
|
||||
messages: [{ role: "user", content: "Hello!" }],
|
||||
});`,
|
||||
},
|
||||
genai: {
|
||||
python: `from google import genai
|
||||
from google.genai.types import HttpOptions
|
||||
|
||||
client = genai.Client(
|
||||
api_key="dummy-api-key", # Handled by Bifrost
|
||||
http_options=HttpOptions(base_url="${baseUrl}/genai")
|
||||
)
|
||||
|
||||
response = client.models.generate_content(
|
||||
model="gemini-2.5-pro", # or "provider/model" for other providers (openai/gpt-4o-mini)
|
||||
contents="Hello!"
|
||||
)`,
|
||||
typescript: `import { GoogleGenerativeAI } from "@google/generative-ai";
|
||||
|
||||
const genAI = new GoogleGenerativeAI("dummy-api-key", { // Handled by Bifrost
|
||||
baseUrl: "${baseUrl}/genai",
|
||||
});
|
||||
|
||||
const model = genAI.getGenerativeModel({ model: "gemini-2.5-pro" }); // or "provider/model" for other providers (openai/gpt-4o-mini)
|
||||
const response = await model.generateContent("Hello!");`,
|
||||
},
|
||||
litellm: {
|
||||
python: `import litellm
|
||||
|
||||
litellm.api_base = "${baseUrl}/litellm"
|
||||
|
||||
response = litellm.completion(
|
||||
model="openai/gpt-4o-mini",
|
||||
messages=[{"role": "user", "content": "Hello!"}]
|
||||
)`,
|
||||
typescript: `import { completion } from "litellm";
|
||||
|
||||
const response = await completion({
|
||||
model: "openai/gpt-4o-mini",
|
||||
messages: [{ role: "user", content: "Hello!" }],
|
||||
api_base: "${baseUrl}/litellm",
|
||||
});`,
|
||||
},
|
||||
langchain: {
|
||||
python: `from langchain_openai import ChatOpenAI
|
||||
from langchain_core.messages import HumanMessage
|
||||
from langchain_core.prompts import ChatPromptTemplate
|
||||
from langchain_core.output_parsers import StrOutputParser
|
||||
|
||||
# Initialize ChatOpenAI with Bifrost
|
||||
llm = ChatOpenAI(
|
||||
model="gpt-4o-mini",
|
||||
api_key="dummy-api-key", # Handled by Bifrost
|
||||
base_url="${baseUrl}/langchain",
|
||||
max_tokens=100,
|
||||
)
|
||||
|
||||
# Simple message
|
||||
messages = [HumanMessage(content="Hello from LangChain!")]
|
||||
response = llm.invoke(messages)
|
||||
|
||||
# Chain with prompt template
|
||||
prompt = ChatPromptTemplate.from_messages([
|
||||
("system", "You are a helpful assistant."),
|
||||
("human", "{input}")
|
||||
])
|
||||
|
||||
chain = prompt | llm | StrOutputParser()
|
||||
result = chain.invoke({"input": "What is LangChain?"})`,
|
||||
typescript: `import { ChatOpenAI } from "@langchain/openai";
|
||||
import { HumanMessage } from "@langchain/core/messages";
|
||||
import { ChatPromptTemplate } from "@langchain/core/prompts";
|
||||
import { StringOutputParser } from "@langchain/core/output_parsers";
|
||||
|
||||
// Initialize ChatOpenAI with Bifrost
|
||||
const llm = new ChatOpenAI({
|
||||
model: "gpt-4o-mini",
|
||||
openAIApiKey: "dummy-api-key", // Handled by Bifrost
|
||||
clientOptions: {
|
||||
baseURL: "${baseUrl}/langchain",
|
||||
},
|
||||
maxTokens: 100,
|
||||
});
|
||||
|
||||
// Simple message
|
||||
const messages = [new HumanMessage("Hello from LangChain!")];
|
||||
const response = await llm.invoke(messages);
|
||||
|
||||
// Chain with prompt template
|
||||
const prompt = ChatPromptTemplate.fromMessages([
|
||||
["system", "You are a helpful assistant."],
|
||||
["human", "{input}"],
|
||||
]);
|
||||
|
||||
const chain = prompt.pipe(llm).pipe(new StringOutputParser());
|
||||
const result = await chain.invoke({ input: "What is LangChain?" });`,
|
||||
},
|
||||
},
|
||||
};
|
||||
}, []);
|
||||
|
||||
const isUnexpectedError = error && error.includes("An unexpected error occurred");
|
||||
|
||||
return (
|
||||
<div className="dark:bg-card flex w-full flex-col items-center justify-center space-y-8 bg-white">
|
||||
{error && (
|
||||
<Alert>
|
||||
<AlertTriangle className="h-4 w-4" />
|
||||
<AlertDescription>
|
||||
{isUnexpectedError ? "Looks like you haven't configured the log store in your config file." : error}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<div className="w-full space-y-6 p-4">
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold">Integrate under 60 seconds</h3>
|
||||
<p className="text-muted-foreground text-sm">Send your first request to get started</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Tabs defaultValue="curl" className="w-full rounded-lg border">
|
||||
<TabsList className="grid h-10 w-full grid-cols-6 rounded-t-lg rounded-b-none">
|
||||
<TabsTrigger value="curl">cURL</TabsTrigger>
|
||||
<TabsTrigger value="openai">OpenAI SDK</TabsTrigger>
|
||||
<TabsTrigger value="anthropic">Anthropic SDK</TabsTrigger>
|
||||
<TabsTrigger value="genai">Google GenAI SDK</TabsTrigger>
|
||||
<TabsTrigger value="litellm">LiteLLM SDK</TabsTrigger>
|
||||
<TabsTrigger value="langchain">LangChain SDK</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="curl" className="px-4">
|
||||
<CodeBlock code={examples.curl} language="bash" readonly={false} />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="openai" className="px-4">
|
||||
<CodeBlock
|
||||
code={examples.sdk.openai[language]}
|
||||
language={language}
|
||||
onLanguageChange={(newLang) => setLanguage(newLang as Language)}
|
||||
showLanguageSelect
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="anthropic" className="px-4">
|
||||
<CodeBlock
|
||||
code={examples.sdk.anthropic[language]}
|
||||
language={language}
|
||||
onLanguageChange={(newLang) => setLanguage(newLang as Language)}
|
||||
showLanguageSelect
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="genai" className="px-4">
|
||||
<CodeBlock
|
||||
code={examples.sdk.genai[language]}
|
||||
language={language}
|
||||
onLanguageChange={(newLang) => setLanguage(newLang as Language)}
|
||||
showLanguageSelect
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="litellm" className="px-4">
|
||||
<CodeBlock
|
||||
code={examples.sdk.litellm[language]}
|
||||
language={language}
|
||||
onLanguageChange={(newLang) => setLanguage(newLang as Language)}
|
||||
showLanguageSelect
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="langchain" className="px-4">
|
||||
<CodeBlock
|
||||
code={examples.sdk.langchain[language]}
|
||||
language={language}
|
||||
onLanguageChange={(newLang) => setLanguage(newLang as Language)}
|
||||
showLanguageSelect
|
||||
/>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user