From 20009aed53d8864c9204d43a17895168a777d2cc Mon Sep 17 00:00:00 2001
From: Ilan Bigio <ilan@openai.com>
Date: Mon, 16 Dec 2024 13:06:08 -0800
Subject: Initial commit

---
 webapp/components/function-calls-panel.tsx | 118 +++++++++++++++++++++++++++++
 1 file changed, 118 insertions(+)
 create mode 100644 webapp/components/function-calls-panel.tsx

(limited to 'webapp/components/function-calls-panel.tsx')

diff --git a/webapp/components/function-calls-panel.tsx b/webapp/components/function-calls-panel.tsx
new file mode 100644
index 0000000..75be81f
--- /dev/null
+++ b/webapp/components/function-calls-panel.tsx
@@ -0,0 +1,118 @@
+import React, { useState } from "react";
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { ScrollArea } from "@/components/ui/scroll-area";
+import { Input } from "@/components/ui/input";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import { Item } from "@/components/types";
+
+type FunctionCallsPanelProps = {
+  items: Item[];
+  ws?: WebSocket | null; // pass down ws from parent
+};
+
+const FunctionCallsPanel: React.FC<FunctionCallsPanelProps> = ({
+  items,
+  ws,
+}) => {
+  const [responses, setResponses] = useState<Record<string, string>>({});
+
+  // Filter function_call items
+  const functionCalls = items.filter((it) => it.type === "function_call");
+
+  // For each function_call, check for a corresponding function_call_output
+  const functionCallsWithStatus = functionCalls.map((call) => {
+    const outputs = items.filter(
+      (it) => it.type === "function_call_output" && it.call_id === call.call_id
+    );
+    const outputItem = outputs[0];
+    const completed = call.status === "completed" || !!outputItem;
+    const response = outputItem ? outputItem.output : undefined;
+    return {
+      ...call,
+      completed,
+      response,
+    };
+  });
+
+  const handleChange = (call_id: string, value: string) => {
+    setResponses((prev) => ({ ...prev, [call_id]: value }));
+  };
+
+  const handleSubmit = (call: Item) => {
+    if (!ws || ws.readyState !== WebSocket.OPEN) return;
+    const call_id = call.call_id || "";
+    ws.send(
+      JSON.stringify({
+        type: "conversation.item.create",
+        item: {
+          type: "function_call_output",
+          call_id: call_id,
+          output: JSON.stringify(responses[call_id] || ""),
+        },
+      })
+    );
+    // Ask the model to continue after providing the tool response
+    ws.send(JSON.stringify({ type: "response.create" }));
+  };
+
+  return (
+    <Card className="flex flex-col h-full">
+      <CardHeader className="space-y-1.5 pb-0">
+        <CardTitle className="text-base font-semibold">
+          Function Calls
+        </CardTitle>
+      </CardHeader>
+      <CardContent className="flex-1 p-4">
+        <ScrollArea className="h-full">
+          <div className="space-y-4">
+            {functionCallsWithStatus.map((call) => (
+              <div
+                key={call.id}
+                className="rounded-lg border bg-card p-4 space-y-3"
+              >
+                <div className="flex items-center justify-between">
+                  <h3 className="font-medium text-sm">{call.name}</h3>
+                  <Badge variant={call.completed ? "default" : "secondary"}>
+                    {call.completed ? "Completed" : "Pending"}
+                  </Badge>
+                </div>
+
+                <div className="text-sm text-muted-foreground font-mono break-all">
+                  {JSON.stringify(call.params)}
+                </div>
+
+                {!call.completed ? (
+                  <div className="space-y-2">
+                    <Input
+                      placeholder="Enter response"
+                      value={responses[call.call_id || ""] || ""}
+                      onChange={(e) =>
+                        handleChange(call.call_id || "", e.target.value)
+                      }
+                    />
+                    <Button
+                      variant="outline"
+                      size="sm"
+                      onClick={() => handleSubmit(call)}
+                      disabled={!responses[call.call_id || ""]}
+                      className="w-full"
+                    >
+                      Submit Response
+                    </Button>
+                  </div>
+                ) : (
+                  <div className="text-sm rounded-md bg-muted p-3">
+                    {JSON.stringify(JSON.parse(call.response || ""))}
+                  </div>
+                )}
+              </div>
+            ))}
+          </div>
+        </ScrollArea>
+      </CardContent>
+    </Card>
+  );
+};
+
+export default FunctionCallsPanel;
-- 
cgit v1.2.3

