Skip to content

Commit 75c63b0

Browse files
Merge pull request #1253 from filintod/filinto/go-conversation-sdk-116
go conversation sdk 116
2 parents 9e83a77 + 9d358e4 commit 75c63b0

File tree

5 files changed

+189
-28
lines changed

5 files changed

+189
-28
lines changed

conversation/go/sdk/README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@ Open a new terminal window and run the multi app run template:
2121
<!-- STEP
2222
name: Run multi app run template
2323
expected_stdout_lines:
24-
- '== APP - conversation == Input sent: What is dapr?'
25-
- '== APP - conversation == Output response: What is dapr?'
24+
- '== APP - conversation-sdk == Input sent: What is dapr?'
25+
- '== APP - conversation-sdk == Output response: What is dapr?'
26+
- '== APP - conversation-sdk == Tool calling input sent: What is the weather like in San Francisco in celsius?'
27+
- '== APP - conversation-sdk == Tool Call: Name: getWeather - Arguments: '
28+
- '== APP - conversation-sdk == Tool Call Output: The weather in San Francisco is 25 degrees Celsius'
2629
expected_stderr_lines:
2730
output_match_mode: substring
2831
match_order: none
@@ -41,8 +44,11 @@ The terminal console output should look similar to this, where:
4144
- The mock LLM echoes `What is dapr?`.
4245

4346
```text
44-
== APP - conversation == Input sent: What is dapr?
45-
== APP - conversation == Output response: What is dapr?
47+
== APP - conversation-sdk == Input sent: What is dapr?
48+
== APP - conversation-sdk == Output response: What is dapr?
49+
== APP - conversation-sdk == Tool calling input sent: What is the weather like in San Francisco in celsius?
50+
== APP - conversation-sdk == Tool Call: Name: getWeather, Arguments: location,unit
51+
== APP - conversation-sdk == Tool Call Output: The weather in San Francisco is 25 degrees Celsius
4652
```
4753

4854
<!-- END_STEP -->

conversation/go/sdk/conversation/conversation.go

Lines changed: 129 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,154 @@ package main
1616

1717
import (
1818
"context"
19+
"encoding/json"
1920
"fmt"
2021
"log"
22+
"strings"
23+
24+
"github.com/invopop/jsonschema"
25+
"google.golang.org/protobuf/encoding/protojson"
26+
"google.golang.org/protobuf/types/known/structpb"
2127

2228
dapr "github.com/dapr/go-sdk/client"
2329
)
2430

31+
// createMapOfArgsForEcho is a helper function to deal with the issue with the echo component not returning args as a map but in csv format
32+
func createMapOfArgsForEcho(s string) ([]byte, error) {
33+
m := map[string]any{}
34+
for _, p := range strings.Split(s, ",") {
35+
m[p] = p
36+
}
37+
return json.Marshal(m)
38+
}
39+
40+
// getWeatherInLocation is an example function to use as a tool call
41+
func getWeatherInLocation(request GetDegreesWeatherRequest, defaultValues GetDegreesWeatherRequest) string {
42+
location := request.Location
43+
unit := request.Unit
44+
if location == "location" {
45+
location = defaultValues.Location
46+
}
47+
if unit == "unit" {
48+
unit = defaultValues.Unit
49+
}
50+
return fmt.Sprintf("The weather in %s is 25 degrees %s", location, unit)
51+
}
52+
53+
type GetDegreesWeatherRequest struct {
54+
Location string `json:"location" jsonschema:"title=Location,description=The location to look up the weather for"`
55+
Unit string `json:"unit" jsonschema:"enum=celsius,enum=fahrenheit,description=Unit"`
56+
}
57+
58+
// GenerateFunctionTool helper method to create jsonschema input
59+
func GenerateFunctionTool[T any](name, description string) (*dapr.ConversationToolsAlpha2, error) {
60+
reflector := jsonschema.Reflector{
61+
AllowAdditionalProperties: false,
62+
DoNotReference: true,
63+
}
64+
var v T
65+
66+
schema := reflector.Reflect(v)
67+
68+
schemaBytes, err := schema.MarshalJSON()
69+
if err != nil {
70+
return nil, err
71+
}
72+
73+
var protoStruct structpb.Struct
74+
if err := protojson.Unmarshal(schemaBytes, &protoStruct); err != nil {
75+
return nil, fmt.Errorf("converting jsonschema to proto Struct: %w", err)
76+
}
77+
78+
return (*dapr.ConversationToolsAlpha2)(&dapr.ConversationToolsFunctionAlpha2{
79+
Name: name,
80+
Description: &description,
81+
Parameters: &protoStruct,
82+
}), nil
83+
}
84+
85+
// createUserMessageInput is a helper method to create user messages in expected proto format
86+
func createUserMessageInput(msg string) *dapr.ConversationInputAlpha2 {
87+
return &dapr.ConversationInputAlpha2{
88+
Messages: []*dapr.ConversationMessageAlpha2{
89+
{
90+
ConversationMessageOfUser: &dapr.ConversationMessageOfUserAlpha2{
91+
Content: []*dapr.ConversationMessageContentAlpha2{
92+
{
93+
Text: &msg,
94+
},
95+
},
96+
},
97+
},
98+
},
99+
}
100+
}
101+
25102
func main() {
26103
client, err := dapr.NewClient()
27104
if err != nil {
28105
panic(err)
29106
}
30107

31-
input := dapr.ConversationInput{
32-
Content: "What is dapr?",
33-
// Role: nil, // Optional
34-
// ScrubPII: nil, // Optional
108+
inputMsg := "What is dapr?"
109+
conversationComponent := "echo"
110+
111+
request := dapr.ConversationRequestAlpha2{
112+
Name: conversationComponent,
113+
Inputs: []*dapr.ConversationInputAlpha2{createUserMessageInput(inputMsg)},
114+
}
115+
116+
fmt.Println("Input sent:", inputMsg)
117+
118+
resp, err := client.ConverseAlpha2(context.Background(), request)
119+
if err != nil {
120+
log.Fatalf("err: %v", err)
35121
}
36122

37-
fmt.Println("Input sent:", input.Content)
123+
fmt.Println("Output response:", resp.Outputs[0].Choices[0].Message.Content)
38124

39-
var conversationComponent = "echo"
125+
tool, err := GenerateFunctionTool[GetDegreesWeatherRequest]("getWeather", "get weather from a location in the given unit")
126+
if err != nil {
127+
log.Fatalf("err: %v", err)
128+
}
40129

41-
request := dapr.NewConversationRequest(conversationComponent, []dapr.ConversationInput{input})
130+
weatherMessage := "Tool calling input sent: What is the weather like in San Francisco in celsius?'"
131+
requestWithTool := dapr.ConversationRequestAlpha2{
132+
Name: conversationComponent,
133+
Inputs: []*dapr.ConversationInputAlpha2{createUserMessageInput(weatherMessage)},
134+
Tools: []*dapr.ConversationToolsAlpha2{tool},
135+
}
42136

43-
resp, err := client.ConverseAlpha1(context.Background(), request)
137+
resp, err = client.ConverseAlpha2(context.Background(), requestWithTool)
44138
if err != nil {
45139
log.Fatalf("err: %v", err)
46140
}
47141

48-
fmt.Println("Output response:", resp.Outputs[0].Result)
142+
fmt.Println(resp.Outputs[0].Choices[0].Message.Content)
143+
for _, toolCalls := range resp.Outputs[0].Choices[0].Message.ToolCalls {
144+
fmt.Printf("Tool Call: Name: %s - Arguments: %v\n", toolCalls.ToolTypes.Name, toolCalls.ToolTypes.Arguments)
145+
146+
// parse the arguments and execute tool
147+
args := []byte(toolCalls.ToolTypes.Arguments)
148+
if conversationComponent == "echo" {
149+
// The echo component does not return a compliant tool calling response in json format but rather returns a csv
150+
args, err = createMapOfArgsForEcho(toolCalls.ToolTypes.Arguments)
151+
if err != nil {
152+
log.Fatalf("err: %v", err)
153+
}
154+
}
155+
156+
// find the tool (only one in this case) and execute
157+
for _, toolInfo := range requestWithTool.Tools {
158+
if toolInfo.Name == toolCalls.ToolTypes.Name && toolInfo.Name == "getWeather" {
159+
var reqArgs GetDegreesWeatherRequest
160+
if err = json.Unmarshal(args, &reqArgs); err != nil {
161+
log.Fatalf("err: %v", err)
162+
}
163+
// execute tool
164+
toolExecutionOutput := getWeatherInLocation(reqArgs, GetDegreesWeatherRequest{Location: "San Francisco", Unit: "Celsius"})
165+
fmt.Printf("Tool Call Output: %s\n", toolExecutionOutput)
166+
}
167+
}
168+
}
49169
}
Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,33 @@
11
module conversation
22

3-
go 1.24.4
3+
go 1.24.6
44

5-
toolchain go1.24.5
6-
7-
require github.com/dapr/go-sdk v1.13.0-rc.1
5+
require (
6+
github.com/dapr/go-sdk v1.13.0
7+
github.com/invopop/jsonschema v0.13.0
8+
google.golang.org/protobuf v1.36.6
9+
)
810

911
require (
10-
github.com/dapr/dapr v1.16.0-rc.3 // indirect
11-
github.com/dapr/kit v0.15.4 // indirect
12+
github.com/bahlo/generic-list-go v0.2.0 // indirect
13+
github.com/buger/jsonparser v1.1.1 // indirect
14+
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
15+
github.com/dapr/dapr v1.16.0 // indirect
16+
github.com/dapr/durabletask-go v0.10.0 // indirect
17+
github.com/dapr/kit v0.16.1 // indirect
18+
github.com/go-logr/logr v1.4.2 // indirect
19+
github.com/go-logr/stdr v1.2.2 // indirect
1220
github.com/google/uuid v1.6.0 // indirect
21+
github.com/mailru/easyjson v0.7.7 // indirect
22+
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
23+
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
1324
go.opentelemetry.io/otel v1.36.0 // indirect
25+
go.opentelemetry.io/otel/metric v1.36.0 // indirect
26+
go.opentelemetry.io/otel/trace v1.36.0 // indirect
1427
golang.org/x/net v0.41.0 // indirect
1528
golang.org/x/sys v0.33.0 // indirect
1629
golang.org/x/text v0.26.0 // indirect
1730
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
1831
google.golang.org/grpc v1.73.0 // indirect
19-
google.golang.org/protobuf v1.36.6 // indirect
2032
gopkg.in/yaml.v3 v3.0.1 // indirect
2133
)

conversation/go/sdk/conversation/go.sum

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
1-
github.com/dapr/dapr v1.16.0-rc.3 h1:D99V20GOhb+bZXH1PngME+wgzIZCcBFOvmaP7DOZxGo=
2-
github.com/dapr/dapr v1.16.0-rc.3/go.mod h1:uyKnxMohSg87LSFzZ/oyuiGSo0+qkzeR0eXncPyIV9c=
3-
github.com/dapr/go-sdk v1.13.0-rc.1 h1:GKvTl38EhxQ3VHuQngMMm8hEaAVHn9gu63CI2HTbI+U=
4-
github.com/dapr/go-sdk v1.13.0-rc.1/go.mod h1:Klfst183A5pb2YZ0KHUCRwdeQuL8RFKX649ILW9K3h4=
5-
github.com/dapr/kit v0.15.4 h1:29DezCR22OuZhXX4yPEc+lqcOf/PNaeAuIEx9nGv394=
6-
github.com/dapr/kit v0.15.4/go.mod h1:HwFsBKEbcyLanWlDZE7u/jnaDCD/tU+n3pkFNUctQNw=
1+
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
2+
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
3+
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
4+
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
5+
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
6+
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
7+
github.com/dapr/dapr v1.16.0 h1:la2WLZM8Myr2Pq3cyrFjHKWDSPYLzGZCs3p502TwBjI=
8+
github.com/dapr/dapr v1.16.0/go.mod h1:ln/mxvNOeqklaDmic4ppsxmnjl2D/oZGKaJy24IwaEY=
9+
github.com/dapr/durabletask-go v0.10.0 h1:vfIivPl4JYd55xZTslDwhA6p6F8ipcNxBtMaupxArr8=
10+
github.com/dapr/durabletask-go v0.10.0/go.mod h1:0Ts4rXp74JyG19gDWPcwNo5V6NBZzhARzHF5XynmA7Q=
11+
github.com/dapr/go-sdk v1.13.0 h1:Qw2BmUonClQ9yK/rrEEaFL1PyDgq616RrvYj0CT67Lk=
12+
github.com/dapr/go-sdk v1.13.0/go.mod h1:RsffVNZitDApmQqoS68tNKGMXDZUjTviAbKZupJSzts=
13+
github.com/dapr/kit v0.16.1 h1:MqLAhHVg8trPy2WJChMZFU7ToeondvxcNHYVvMDiVf4=
14+
github.com/dapr/kit v0.16.1/go.mod h1:40ZWs5P6xfYf7O59XgwqZkIyDldTIXlhTQhGop8QoSM=
715
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
816
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
17+
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
918
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
1019
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
1120
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
@@ -16,10 +25,23 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
1625
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
1726
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
1827
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
28+
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
29+
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
30+
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
31+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
32+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
33+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
34+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
35+
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
36+
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
1937
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
2038
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
39+
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
40+
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
2141
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
2242
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
43+
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
44+
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
2345
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
2446
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
2547
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
@@ -44,7 +66,8 @@ google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
4466
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
4567
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
4668
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
47-
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
4869
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
70+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
71+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
4972
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
5073
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

conversation/go/sdk/dapr.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ common:
33
resourcesPath: ../../components/
44
apps:
55
- appDirPath: ./conversation/
6-
appID: conversation
6+
appID: conversation-sdk
77
command: ["go", "run", "."]

0 commit comments

Comments
 (0)