Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions conversation/go/sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ Open a new terminal window and run the multi app run template:
<!-- STEP
name: Run multi app run template
expected_stdout_lines:
- '== APP - conversation == Input sent: What is dapr?'
- '== APP - conversation == Output response: What is dapr?'
- '== APP - conversation-sdk == Input sent: What is dapr?'
- '== APP - conversation-sdk == Output response: What is dapr?'
- '== APP - conversation-sdk == Tool calling input sent: What is the weather like in San Francisco in celsius?'
- '== APP - conversation-sdk == Tool Call: Name: getWeather - Arguments: '
- '== APP - conversation-sdk == Tool Call Output: The weather in San Francisco is 25 degrees Celsius'
expected_stderr_lines:
output_match_mode: substring
match_order: none
Expand All @@ -41,8 +44,11 @@ The terminal console output should look similar to this, where:
- The mock LLM echoes `What is dapr?`.

```text
== APP - conversation == Input sent: What is dapr?
== APP - conversation == Output response: What is dapr?
== APP - conversation-sdk == Input sent: What is dapr?
== APP - conversation-sdk == Output response: What is dapr?
== APP - conversation-sdk == Tool calling input sent: What is the weather like in San Francisco in celsius?
== APP - conversation-sdk == Tool Call: Name: getWeather, Arguments: location,unit
== APP - conversation-sdk == Tool Call Output: The weather in San Francisco is 25 degrees Celsius
```

<!-- END_STEP -->
Expand Down
138 changes: 129 additions & 9 deletions conversation/go/sdk/conversation/conversation.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,154 @@ package main

import (
"context"
"encoding/json"
"fmt"
"log"
"strings"

"github.com/invopop/jsonschema"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/types/known/structpb"

dapr "github.com/dapr/go-sdk/client"
)

// createMapOfArgsForEcho is a helper function to deal with the issue with the echo component not returning args as a map but in csv format
func createMapOfArgsForEcho(s string) ([]byte, error) {
m := map[string]any{}
for _, p := range strings.Split(s, ",") {
m[p] = p
}
return json.Marshal(m)
}

// getWeatherInLocation is an example function to use as a tool call
func getWeatherInLocation(request GetDegreesWeatherRequest, defaultValues GetDegreesWeatherRequest) string {
location := request.Location
unit := request.Unit
if location == "location" {
location = defaultValues.Location
}
if unit == "unit" {
unit = defaultValues.Unit
}
return fmt.Sprintf("The weather in %s is 25 degrees %s", location, unit)
}

type GetDegreesWeatherRequest struct {
Location string `json:"location" jsonschema:"title=Location,description=The location to look up the weather for"`
Unit string `json:"unit" jsonschema:"enum=celsius,enum=fahrenheit,description=Unit"`
}

// GenerateFunctionTool helper method to create jsonschema input
func GenerateFunctionTool[T any](name, description string) (*dapr.ConversationToolsAlpha2, error) {
reflector := jsonschema.Reflector{
AllowAdditionalProperties: false,
DoNotReference: true,
}
var v T

schema := reflector.Reflect(v)

schemaBytes, err := schema.MarshalJSON()
if err != nil {
return nil, err
}

var protoStruct structpb.Struct
if err := protojson.Unmarshal(schemaBytes, &protoStruct); err != nil {
return nil, fmt.Errorf("converting jsonschema to proto Struct: %w", err)
}

return (*dapr.ConversationToolsAlpha2)(&dapr.ConversationToolsFunctionAlpha2{
Name: name,
Description: &description,
Parameters: &protoStruct,
}), nil
}

// createUserMessageInput is a helper method to create user messages in expected proto format
func createUserMessageInput(msg string) *dapr.ConversationInputAlpha2 {
return &dapr.ConversationInputAlpha2{
Messages: []*dapr.ConversationMessageAlpha2{
{
ConversationMessageOfUser: &dapr.ConversationMessageOfUserAlpha2{
Content: []*dapr.ConversationMessageContentAlpha2{
{
Text: &msg,
},
},
},
},
},
}
}

func main() {
client, err := dapr.NewClient()
if err != nil {
panic(err)
}

input := dapr.ConversationInput{
Content: "What is dapr?",
// Role: nil, // Optional
// ScrubPII: nil, // Optional
inputMsg := "What is dapr?"
conversationComponent := "echo"

request := dapr.ConversationRequestAlpha2{
Name: conversationComponent,
Inputs: []*dapr.ConversationInputAlpha2{createUserMessageInput(inputMsg)},
}

fmt.Println("Input sent:", inputMsg)

resp, err := client.ConverseAlpha2(context.Background(), request)
if err != nil {
log.Fatalf("err: %v", err)
}

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

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

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

resp, err := client.ConverseAlpha1(context.Background(), request)
resp, err = client.ConverseAlpha2(context.Background(), requestWithTool)
if err != nil {
log.Fatalf("err: %v", err)
}

fmt.Println("Output response:", resp.Outputs[0].Result)
fmt.Println(resp.Outputs[0].Choices[0].Message.Content)
for _, toolCalls := range resp.Outputs[0].Choices[0].Message.ToolCalls {
fmt.Printf("Tool Call: Name: %s - Arguments: %v\n", toolCalls.ToolTypes.Name, toolCalls.ToolTypes.Arguments)

// parse the arguments and execute tool
args := []byte(toolCalls.ToolTypes.Arguments)
if conversationComponent == "echo" {
// The echo component does not return a compliant tool calling response in json format but rather returns a csv
args, err = createMapOfArgsForEcho(toolCalls.ToolTypes.Arguments)
if err != nil {
log.Fatalf("err: %v", err)
}
}

// find the tool (only one in this case) and execute
for _, toolInfo := range requestWithTool.Tools {
if toolInfo.Name == toolCalls.ToolTypes.Name && toolInfo.Name == "getWeather" {
var reqArgs GetDegreesWeatherRequest
if err = json.Unmarshal(args, &reqArgs); err != nil {
log.Fatalf("err: %v", err)
}
// execute tool
toolExecutionOutput := getWeatherInLocation(reqArgs, GetDegreesWeatherRequest{Location: "San Francisco", Unit: "Celsius"})
fmt.Printf("Tool Call Output: %s\n", toolExecutionOutput)
}
}
}
}
26 changes: 19 additions & 7 deletions conversation/go/sdk/conversation/go.mod
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
module conversation

go 1.24.4
go 1.24.6

toolchain go1.24.5

require github.com/dapr/go-sdk v1.13.0-rc.1
require (
github.com/dapr/go-sdk v1.13.0
github.com/invopop/jsonschema v0.13.0
google.golang.org/protobuf v1.36.6
)

require (
github.com/dapr/dapr v1.16.0-rc.3 // indirect
github.com/dapr/kit v0.15.4 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/dapr/dapr v1.16.0 // indirect
github.com/dapr/durabletask-go v0.10.0 // indirect
github.com/dapr/kit v0.16.1 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.26.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/grpc v1.73.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
37 changes: 30 additions & 7 deletions conversation/go/sdk/conversation/go.sum
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
github.com/dapr/dapr v1.16.0-rc.3 h1:D99V20GOhb+bZXH1PngME+wgzIZCcBFOvmaP7DOZxGo=
github.com/dapr/dapr v1.16.0-rc.3/go.mod h1:uyKnxMohSg87LSFzZ/oyuiGSo0+qkzeR0eXncPyIV9c=
github.com/dapr/go-sdk v1.13.0-rc.1 h1:GKvTl38EhxQ3VHuQngMMm8hEaAVHn9gu63CI2HTbI+U=
github.com/dapr/go-sdk v1.13.0-rc.1/go.mod h1:Klfst183A5pb2YZ0KHUCRwdeQuL8RFKX649ILW9K3h4=
github.com/dapr/kit v0.15.4 h1:29DezCR22OuZhXX4yPEc+lqcOf/PNaeAuIEx9nGv394=
github.com/dapr/kit v0.15.4/go.mod h1:HwFsBKEbcyLanWlDZE7u/jnaDCD/tU+n3pkFNUctQNw=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/dapr/dapr v1.16.0 h1:la2WLZM8Myr2Pq3cyrFjHKWDSPYLzGZCs3p502TwBjI=
github.com/dapr/dapr v1.16.0/go.mod h1:ln/mxvNOeqklaDmic4ppsxmnjl2D/oZGKaJy24IwaEY=
github.com/dapr/durabletask-go v0.10.0 h1:vfIivPl4JYd55xZTslDwhA6p6F8ipcNxBtMaupxArr8=
github.com/dapr/durabletask-go v0.10.0/go.mod h1:0Ts4rXp74JyG19gDWPcwNo5V6NBZzhARzHF5XynmA7Q=
github.com/dapr/go-sdk v1.13.0 h1:Qw2BmUonClQ9yK/rrEEaFL1PyDgq616RrvYj0CT67Lk=
github.com/dapr/go-sdk v1.13.0/go.mod h1:RsffVNZitDApmQqoS68tNKGMXDZUjTviAbKZupJSzts=
github.com/dapr/kit v0.16.1 h1:MqLAhHVg8trPy2WJChMZFU7ToeondvxcNHYVvMDiVf4=
github.com/dapr/kit v0.16.1/go.mod h1:40ZWs5P6xfYf7O59XgwqZkIyDldTIXlhTQhGop8QoSM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
Expand All @@ -16,10 +25,23 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
Expand All @@ -44,7 +66,8 @@ google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
2 changes: 1 addition & 1 deletion conversation/go/sdk/dapr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ common:
resourcesPath: ../../components/
apps:
- appDirPath: ./conversation/
appID: conversation
appID: conversation-sdk
command: ["go", "run", "."]
Loading