summaryrefslogtreecommitdiff
path: root/webapp/components/function-calls-panel.tsx
blob: 75be81fb5853a39027f5329586e7db63fbc8d511 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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;