Skip to content

Commit 0572ccb

Browse files
Merge pull request #1231 from MyMirelHub/go-http-tool-calling
Go http tool calling
2 parents 3d1cfd3 + a6ce71a commit 0572ccb

File tree

2 files changed

+145
-7
lines changed

2 files changed

+145
-7
lines changed

conversation/go/http/README.md

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Visit [this](https://docs.dapr.io/developing-applications/building-blocks/conver
88
99
This quickstart includes one app:
1010

11-
- `conversation.go`, responsible for sending an input to the underlying LLM and retrieving an output.
11+
- `conversation.go`, responsible for sending an input to the underlying LLM and retrieving an output. It includes a secondary conversation request to showcase tool calling to the underlying LLM.
1212

1313
## Run the app with the template file
1414

@@ -23,6 +23,9 @@ name: Run multi app run template
2323
expected_stdout_lines:
2424
- '== APP - conversation == Input sent: What is dapr?'
2525
- '== APP - conversation == Output response: What is dapr?'
26+
- '== APP - conversation == Tool calling input sent: What is the weather like in San Francisco in celsius?'
27+
- '== APP - conversation == Output message: What is the weather like in San Francisco in celsius?'
28+
- '== APP - conversation == Tool calls detected:'
2629
expected_stderr_lines:
2730
output_match_mode: substring
2831
match_order: none
@@ -37,18 +40,55 @@ dapr run -f .
3740

3841
The terminal console output should look similar to this, where:
3942

40-
- The app sends an input `What is dapr?` to the `echo` Component mock LLM.
43+
- The app first sends an input `What is dapr?` to the `echo` Component mock LLM.
4144
- The mock LLM echoes `What is dapr?`.
45+
- The app then sends a weather request to the component with tools available to the LLM.
46+
- The LLM will either respond back with a tool call for the user, or an ask for more information.
4247

4348
```text
4449
== APP - conversation == Input sent: What is dapr?
4550
== APP - conversation == Output response: What is dapr?
4651
```
4752

53+
- The app then sends an input `What is the weather like in San Francisco in celsius?` to the `echo` Component mock LLM.
54+
- The mock LLM echoes `What is the weather like in San Francisco in celsius?` and calls the `get_weather` tool.
55+
- The echo Component returns the tool call information.
56+
57+
```text
58+
== APP - conversation == Tool calling input sent: What is the weather like in San Francisco in celsius?
59+
== APP - conversation == Output message: What is the weather like in San Francisco in celsius?
60+
== APP - conversation == Tool calls detected:
61+
== APP - conversation == Tool call: map[function:map[arguments:unit,location name:get_weather] id:0]
62+
```
63+
4864
<!-- END_STEP -->
4965

5066
2. Stop and clean up application processes.
5167

5268
```bash
5369
dapr stop -f .
5470
```
71+
72+
## Run the app with the Dapr CLI
73+
74+
1. Run the application:
75+
76+
```bash
77+
dapr run --app-id conversation --resources-path ../../../components -- go run conversation.go
78+
```
79+
80+
The terminal console output should look similar to this, where:
81+
82+
- The app first sends an input `What is dapr?` to the `echo` Component mock LLM.
83+
- The mock LLM echoes `What is dapr?`.
84+
- The app then sends an input `What is the weather like in San Francisco in celsius?` to the `echo` Component mock LLM.
85+
- The mock LLM echoes `What is the weather like in San Francisco in celsius?`
86+
87+
```text
88+
== APP - conversation == Input sent: What is dapr?
89+
== APP - conversation == Output response: What is dapr?
90+
== APP - conversation == Tool calling input sent: What is the weather like in San Francisco in celsius?
91+
== APP - conversation == Output message: What is the weather like in San Francisco in celsius?
92+
== APP - conversation == Tool calls detected:
93+
== APP - conversation == Tool call: map[function:map[arguments:unit,location name:get_weather] id:0]
94+
```

conversation/go/http/conversation/conversation.go

Lines changed: 103 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,20 @@ func main() {
4343

4444
var inputBody = `{
4545
"name": "echo",
46-
"inputs": [{"content":"What is dapr?"}],
46+
"inputs": [{
47+
"messages": [{
48+
"of_user": {
49+
"content": [{
50+
"text": "What is dapr?"
51+
}]
52+
}
53+
}]
54+
}],
4755
"parameters": {},
4856
"metadata": {}
49-
}`
57+
}`
5058

51-
reqURL := daprHost + ":" + daprHttpPort + "/v1.0-alpha1/conversation/" + conversationComponentName + "/converse"
59+
reqURL := daprHost + ":" + daprHttpPort + "/v1.0-alpha2/conversation/" + conversationComponentName + "/converse"
5260

5361
req, err := http.NewRequest("POST", reqURL, strings.NewReader(inputBody))
5462
if err != nil {
@@ -73,12 +81,102 @@ func main() {
7381
}
7482

7583
// Unmarshal the response
76-
var data map[string][]map[string]string
84+
var data map[string]any
7785
if err := json.Unmarshal(bodyBytes, &data); err != nil {
7886
log.Fatal(err)
7987
}
8088

81-
result := data["outputs"][0]["result"]
89+
// Navigate the new response structure: outputs[0].choices[0].message.content
90+
outputs := data["outputs"].([]any)
91+
output := outputs[0].(map[string]any)
92+
choices := output["choices"].([]any)
93+
choice := choices[0].(map[string]any)
94+
message := choice["message"].(map[string]any)
95+
result := message["content"].(string)
96+
8297
fmt.Println("Output response:", result)
8398

99+
// Tool calling example
100+
var toolCallBody = `{
101+
"name": "echo",
102+
"inputs": [{
103+
"messages": [{
104+
"of_user": {
105+
"content": [{
106+
"text": "What is the weather like in San Francisco in celsius?"
107+
}]
108+
}
109+
}]
110+
}],
111+
"parameters": {},
112+
"metadata": {},
113+
"tools": [{
114+
"function": {
115+
"name": "get_weather",
116+
"description": "Get the current weather for a location",
117+
"parameters": {
118+
"type": "object",
119+
"properties": {
120+
"location": {
121+
"type": "string",
122+
"description": "The city and state, e.g. San Francisco, CA"
123+
},
124+
"unit": {
125+
"type": "string",
126+
"enum": ["celsius", "fahrenheit"],
127+
"description": "The temperature unit to use"
128+
}
129+
},
130+
"required": ["location"]
131+
}
132+
}
133+
}],
134+
"toolChoice": "auto"
135+
}`
136+
137+
req2, err := http.NewRequest("POST", reqURL, strings.NewReader(toolCallBody))
138+
if err != nil {
139+
log.Fatal(err.Error())
140+
}
141+
142+
req2.Header.Set("Content-Type", "application/json")
143+
144+
res2, err := client.Do(req2)
145+
if err != nil {
146+
log.Fatal(err)
147+
}
148+
149+
defer res2.Body.Close()
150+
151+
fmt.Println("\nTool calling input sent: What is the weather like in San Francisco in celsius?")
152+
153+
bodyBytes2, err := io.ReadAll(res2.Body)
154+
if err != nil {
155+
log.Fatal(err)
156+
}
157+
158+
var data2 map[string]any
159+
if err := json.Unmarshal(bodyBytes2, &data2); err != nil {
160+
log.Fatal(err)
161+
}
162+
163+
// Parse tool calling response
164+
outputs2 := data2["outputs"].([]any)
165+
output2 := outputs2[0].(map[string]any)
166+
choices2 := output2["choices"].([]any)
167+
choice2 := choices2[0].(map[string]any)
168+
message2 := choice2["message"].(map[string]any)
169+
170+
if content, ok := message2["content"].(string); ok && content != "" {
171+
fmt.Println("Output message:", content)
172+
}
173+
174+
if toolCalls, ok := message2["toolCalls"].([]any); ok && len(toolCalls) > 0 {
175+
fmt.Println("Tool calls detected:")
176+
for _, tc := range toolCalls {
177+
fmt.Printf("Tool call: %v\n", tc)
178+
}
179+
} else {
180+
fmt.Println("No tool calls in response")
181+
}
84182
}

0 commit comments

Comments
 (0)