diff --git a/proxy/go.mod b/proxy/go.mod index 76c5712..efc5e30 100644 --- a/proxy/go.mod +++ b/proxy/go.mod @@ -5,11 +5,8 @@ go 1.20 require ( github.com/gorilla/handlers v1.5.2 github.com/gorilla/mux v1.8.1 + github.com/joho/godotenv v1.5.1 github.com/mattn/go-sqlite3 v1.14.28 ) -require ( - github.com/felixge/httpsnoop v1.0.3 // indirect - github.com/joho/godotenv v1.5.1 // indirect - github.com/mattn/go-sqlite3 v1.14.28 // indirect -) +require github.com/felixge/httpsnoop v1.0.3 // indirect diff --git a/web/app/components/RequestDetailContent.tsx b/web/app/components/RequestDetailContent.tsx index 5140908..6aaccca 100644 --- a/web/app/components/RequestDetailContent.tsx +++ b/web/app/components/RequestDetailContent.tsx @@ -17,7 +17,8 @@ import { Wifi, Calendar, List, - FileText + FileText, + Wrench } from 'lucide-react'; import { MessageContent } from './MessageContent'; import { formatJSON } from '../utils/formatters'; @@ -39,6 +40,15 @@ interface Request { type: string; cache_control?: { type: string }; }>; + tools?: Array<{ + name: string; + description: string; + input_schema?: { + type: string; + properties?: Record; + required?: string[]; + }; + }>; max_tokens?: number; temperature?: number; stream?: boolean; @@ -242,6 +252,36 @@ export default function RequestDetailContent({ request, onGrade }: RequestDetail )} + {/* Tools */} + {request.body.tools && request.body.tools.length > 0 && ( +
+
toggleSection('tools')} + > +
+

+ + Available Tools + + {request.body.tools.length} tools + +

+ +
+
+ {expandedSections.tools && ( +
+ {request.body.tools.map((tool, index) => ( + + ))} +
+ )} +
+ )} + {/* Conversation */} {request.body.messages && (
@@ -786,4 +826,108 @@ function ResponseDetails({ response }: { response: NonNullable ); +} + +// Tool Card Component +function ToolCard({ tool, index }: { tool: any; index: number }) { + const [expanded, setExpanded] = useState(false); + const [copiedSchema, setCopiedSchema] = useState(false); + + const handleCopySchema = async () => { + try { + await navigator.clipboard.writeText(formatJSON(tool.input_schema)); + setCopiedSchema(true); + setTimeout(() => setCopiedSchema(false), 2000); + } catch (error) { + console.error('Failed to copy schema:', error); + } + }; + + // Parse description to identify code blocks and format them + const formatDescription = (description: string) => { + // Split by code blocks (text between backticks) + const parts = description.split(/(`[^`]+`)/g); + + return parts.map((part, i) => { + if (part.startsWith('`') && part.endsWith('`')) { + // Code inline + const code = part.slice(1, -1); + return ( + + {code} + + ); + } + + // Return non-code parts as plain text + return {part}; + }); + }; + + const isLongDescription = tool.description.length > 300; + const displayDescription = expanded ? tool.description : tool.description.slice(0, 300); + + return ( +
+
+
+
+
+ +
+
+
{tool.name}
+ Tool #{index + 1} +
+
+
+ +
+
+
+ {formatDescription(displayDescription)} + {isLongDescription && !expanded && '...'} +
+ {isLongDescription && ( + + )} +
+
+ + {tool.input_schema && ( +
+
+
+ + + Input Schema + + +
+
+
+                  {formatJSON(tool.input_schema)}
+                
+
+
+
+ )} +
+
+ ); } \ No newline at end of file diff --git a/web/app/routes/_index.tsx b/web/app/routes/_index.tsx index 009ac5e..d1dece2 100644 --- a/web/app/routes/_index.tsx +++ b/web/app/routes/_index.tsx @@ -61,6 +61,15 @@ interface Request { type: string; cache_control?: { type: string }; }>; + tools?: Array<{ + name: string; + description: string; + input_schema?: { + type: string; + properties?: Record; + required?: string[]; + }; + }>; max_tokens?: number; temperature?: number; stream?: boolean; @@ -231,13 +240,16 @@ export default function Index() { } }; - const loadConversations = async (loadMore = false) => { + const loadConversations = async (modelFilter: string = "all", loadMore = false) => { setIsFetching(true); const pageToFetch = loadMore ? conversationsCurrentPage + 1 : 1; try { const url = new URL('/api/conversations', window.location.origin); url.searchParams.append("page", pageToFetch.toString()); url.searchParams.append("limit", itemsPerPage.toString()); + if (modelFilter !== "all") { + url.searchParams.append("model", modelFilter); + } const response = await fetch(url.toString()); if (!response.ok) { @@ -500,7 +512,7 @@ export default function Index() { if (viewMode === 'requests') { loadRequests(modelFilter); } else { - loadConversations(); + loadConversations(modelFilter); } }, [viewMode, modelFilter]); @@ -807,7 +819,7 @@ export default function Index() { {hasMoreConversations && (