-
Notifications
You must be signed in to change notification settings - Fork 70
feat: jsonrpc tool call card #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
9625b9a
26c8c59
4fdd38f
f0f4342
7208e15
6d3d79b
cab0003
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -0,0 +1,74 @@ | ||||
| import { cn } from "@heroui/react"; | ||||
| import { JsonRpcResult } from "./utils/common"; | ||||
| import MarkdownComponent from "../../markdown"; | ||||
| import { LoadingIndicator } from "../../loading-indicator"; | ||||
| import { useState } from "react"; | ||||
|
|
||||
| type JsonRpcProps = { | ||||
| functionName: string; | ||||
| jsonRpcResult: JsonRpcResult; | ||||
| preparing: boolean; | ||||
| animated: boolean; | ||||
| }; | ||||
|
|
||||
| export const JsonRpc = ({ functionName, jsonRpcResult, preparing, animated }: JsonRpcProps) => { | ||||
| const [isCollapsed, setIsCollapsed] = useState(false); | ||||
|
|
||||
| if (preparing) { | ||||
| return ( | ||||
| <div className={cn("tool-card", { animated: animated })}> | ||||
| <div className="flex items-center justify-between"> | ||||
| <h3 className="tool-card-title tool-card-jsonrpc">{functionName}</h3> | ||||
| </div> | ||||
| <LoadingIndicator text="Processing ..." estimatedSeconds={300} /> | ||||
| </div> | ||||
| ); | ||||
| } | ||||
|
|
||||
| const toggleCollapse = () => { | ||||
| setIsCollapsed(!isCollapsed); | ||||
| }; | ||||
|
|
||||
| return ( | ||||
| <div className={cn("tool-card noselect narrow", { animated: animated })}> | ||||
| <div className="flex items-center justify-between cursor-pointer" onClick={toggleCollapse}> | ||||
| <h3 className="tool-card-title tool-card-jsonrpc">{functionName}</h3> | ||||
| <button | ||||
| onClick={toggleCollapse} | ||||
|
||||
| onClick={toggleCollapse} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import { cn } from "@heroui/react"; | ||
|
|
||
| type UnknownJsonRpcProps = { | ||
| functionName: string; | ||
| message: string; | ||
| animated: boolean; | ||
| }; | ||
|
|
||
| export const UnknownJsonRpc = ({ functionName, message, animated }: UnknownJsonRpcProps) => { | ||
| return ( | ||
| <div className={cn("tool-card", { animated: animated })}> | ||
| <h3 className="text-xs font-semibold font-sans text-primary-700 uppercase tracking-wider mb-1"> | ||
| Unknown JsonRPC "{functionName}" | ||
| </h3> | ||
| <span className="text-xs text-primary-600">{message}</span> | ||
| </div> | ||
| ); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| export type JsonRpcResult = { | ||
| jsonrpc: string; | ||
| id: number; | ||
| result?: { | ||
| content: Array<{ | ||
| type: string; | ||
| text: string; | ||
| }>; | ||
| }; | ||
| error?: { | ||
| code: number; | ||
| message: string; | ||
| } | ||
| } | ||
|
|
||
| export const UNKNOWN_JSONRPC_RESULT: JsonRpcResult = { | ||
| jsonrpc: "2.0", | ||
| id: -1, | ||
| error: { | ||
| code: -1, | ||
| message: "Unknown JSONRPC result", | ||
| }, | ||
| } | ||
|
|
||
| const isValidJsonRpcResult = (obj: any): obj is JsonRpcResult => { | ||
| // Check if obj is an object and not null | ||
| if (typeof obj !== 'object' || obj === null) { | ||
| return false; | ||
| } | ||
|
|
||
| // Check required properties | ||
| if (typeof obj.jsonrpc !== 'string' || typeof obj.id !== 'number') { | ||
| return false; | ||
| } | ||
|
|
||
| // Check that either result or error is present (but not both required) | ||
| const hasResult = obj.result !== undefined; | ||
| const hasError = obj.error !== undefined; | ||
|
|
||
| // Validate result structure if present | ||
| if (hasResult) { | ||
| if (typeof obj.result !== 'object' || obj.result === null) { | ||
| return false; | ||
| } | ||
| if (obj.result.content !== undefined) { | ||
| if (!Array.isArray(obj.result.content)) { | ||
| return false; | ||
| } | ||
| // Validate each content item | ||
| for (const item of obj.result.content) { | ||
| if (typeof item !== 'object' || item === null || | ||
| typeof item.type !== 'string' || typeof item.text !== 'string') { | ||
| return false; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Validate error structure if present | ||
| if (hasError) { | ||
| if (typeof obj.error !== 'object' || obj.error === null || | ||
| typeof obj.error.code !== 'number' || typeof obj.error.message !== 'string') { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| return true; | ||
| }; | ||
|
|
||
| export const parseJsonRpcResult = (message: string): JsonRpcResult | undefined => { | ||
| try { | ||
| const json = JSON.parse(message); | ||
|
|
||
| // Validate the structure before casting | ||
| if (isValidJsonRpcResult(json)) { | ||
| return json; | ||
| } | ||
|
|
||
| return undefined; | ||
| } catch (error) { | ||
| return undefined; | ||
| } | ||
| } | ||
|
Comment on lines
+70
to
+83
|
||
Uh oh!
There was an error while loading. Please reload this page.