diff --git a/configuration/python/http/order-processor/requirements.txt b/configuration/python/http/order-processor/requirements.txt index 023ec6359..90755d9c4 100644 --- a/configuration/python/http/order-processor/requirements.txt +++ b/configuration/python/http/order-processor/requirements.txt @@ -1,2 +1,3 @@ requests -flask \ No newline at end of file +flask +urllib3>=2.2.2 # not directly required, pinned by Snyk to avoid a vulnerability \ No newline at end of file diff --git a/conversation/components/conversation.yaml b/conversation/components/conversation.yaml new file mode 100644 index 000000000..efb651fef --- /dev/null +++ b/conversation/components/conversation.yaml @@ -0,0 +1,7 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: echo +spec: + type: conversation.echo + version: v1 diff --git a/conversation/csharp/http/README.md b/conversation/csharp/http/README.md new file mode 100644 index 000000000..b1d8a3144 --- /dev/null +++ b/conversation/csharp/http/README.md @@ -0,0 +1,85 @@ +# Dapr Conversation API (C# HTTP) + +In this quickstart, you'll send an input to a mock Large Language Model (LLM) using Dapr's Conversation API. This API is responsible for providing one consistent API entry point to talk to underlying LLM providers. + +Visit [this](https://v1-15.docs.dapr.io/developing-applications/building-blocks/conversation/conversation-overview/) link for more information about Dapr and the Conversation API. + +> **Note:** This example leverages HTTP `requests` only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/). + +This quickstart includes one app: + +- Conversation, responsible for sending an input to the underlying LLM and retrieving an output. + +## Run the app with the template file + +This section shows how to run the application using the [multi-app run template file](https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-overview/) and Dapr CLI with `dapr run -f .`. + +This example uses the default LLM Component provided by Dapr which simply echoes the input provided, for testing purposes. Integrate with popular LLM models by using one of the other [supported conversation components](https://v1-15.docs.dapr.io/reference/components-reference/supported-conversation/). + +Open a new terminal window and run the multi app run template: + + + +```bash +dapr run -f . +``` + +The terminal console output should look similar to this, where: + +- The app sends an input `What is dapr?` to the `echo` Component mock LLM. +- The mock LLM echoes `What is dapr?`. + +```text +== APP - conversation == Input sent: What is dapr? +== APP - conversation == Output response: What is dapr? +``` + + + +2. Stop and clean up application processes. + + + +```bash +dapr stop -f . +``` + + + +## Run the app individually + +1. Open a terminal and run the `conversation` app. Build the dependencies if you haven't already. + +```bash +cd ./conversation +dotnet build +``` + +2. Run the Dapr process alongside the application. + +```bash +dapr run --app-id conversation --resources-path ../../../components/ -- dotnet run +``` + +The terminal console output should look similar to this, where: + +- The app sends an input `What is dapr?` to the `echo` Component mock LLM. +- The mock LLM echoes `What is dapr?`. + +```text +== APP - conversation == Input sent: What is dapr? +== APP - conversation == Output response: What is dapr? +``` diff --git a/conversation/csharp/http/conversation/Program.cs b/conversation/csharp/http/conversation/Program.cs new file mode 100644 index 000000000..4ea1cbaf2 --- /dev/null +++ b/conversation/csharp/http/conversation/Program.cs @@ -0,0 +1,67 @@ +/* +Copyright 2024 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System.Net.Http; +using System.Text.Json; +using System.Text; + +class Program +{ + private const string ConversationComponentName = "echo"; + + static async Task Main(string[] args) + { + var daprHost = Environment.GetEnvironmentVariable("DAPR_HOST") ?? "http://localhost"; + var daprHttpPort = Environment.GetEnvironmentVariable("DAPR_HTTP_PORT") ?? "3500"; + + var client = new HttpClient + { + Timeout = TimeSpan.FromSeconds(15) + }; + + var inputBody = new + { + name = "echo", + inputs = new[] { new { message = "What is dapr?" } }, + parameters = new { }, + metadata = new { } + }; + + var daprUrl = $"{daprHost}:{daprHttpPort}/v1.0-alpha1/conversation/{ConversationComponentName}/converse"; + + try + { + var content = new StringContent(JsonSerializer.Serialize(inputBody), Encoding.UTF8, "application/json"); + + // Send a request to the echo mock LLM component + var response = await client.PostAsync(daprUrl, content); + response.EnsureSuccessStatusCode(); + + Console.WriteLine("Input sent: " + inputBody.inputs[0].message); + + var responseBody = await response.Content.ReadAsStringAsync(); + + // Parse the response + var data = JsonSerializer.Deserialize>>>(responseBody); + var result = data?["outputs"]?[0]?["result"]; + + Console.WriteLine("Output response: " + result); + } + catch (Exception ex) + { + Console.WriteLine("Error: " + ex.Message); + } + } +} diff --git a/conversation/csharp/http/conversation/Program.csproj b/conversation/csharp/http/conversation/Program.csproj new file mode 100644 index 000000000..47cf3be7e --- /dev/null +++ b/conversation/csharp/http/conversation/Program.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + diff --git a/conversation/csharp/http/dapr.yaml b/conversation/csharp/http/dapr.yaml new file mode 100644 index 000000000..d9d9b265a --- /dev/null +++ b/conversation/csharp/http/dapr.yaml @@ -0,0 +1,8 @@ +version: 1 +common: + resourcesPath: ../../components/ +apps: + - appDirPath: ./conversation/ + appID: conversation + daprHTTPPort: 3500 + command: ["dotnet", "run"] \ No newline at end of file diff --git a/conversation/csharp/http/makefile b/conversation/csharp/http/makefile new file mode 100644 index 000000000..e7a8826bf --- /dev/null +++ b/conversation/csharp/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/conversation/csharp/sdk/README.md b/conversation/csharp/sdk/README.md new file mode 100644 index 000000000..6a5026f2a --- /dev/null +++ b/conversation/csharp/sdk/README.md @@ -0,0 +1,85 @@ +# Dapr Conversation API (C# SDK) + +In this quickstart, you'll send an input to a mock Large Language Model (LLM) using Dapr's Conversation API. This API is responsible for providing one consistent API entry point to talk to underlying LLM providers. + +Visit [this](https://v1-15.docs.dapr.io/developing-applications/building-blocks/conversation/conversation-overview/) link for more information about Dapr and the Conversation API. + +> **Note:** This example leverages the Dapr SDK. If you are looking for the example using the HTTP API [click here](../http/). + +This quickstart includes one app: + +- Conversation, responsible for sending an input to the underlying LLM and retrieving an output. + +## Run the app with the template file + +This section shows how to run the application using the [multi-app run template file](https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-overview/) and Dapr CLI with `dapr run -f .`. + +This example uses the default LLM Component provided by Dapr which simply echoes the input provided, for testing purposes. Integrate with popular LLM models by using one of the other [supported conversation components](https://v1-15.docs.dapr.io/reference/components-reference/supported-conversation/). + +Open a new terminal window and run the multi app run template: + + + +```bash +dapr run -f . +``` + +The terminal console output should look similar to this, where: + +- The app sends an input `What is dapr?` to the `echo` Component mock LLM. +- The mock LLM echoes `What is dapr?`. + +```text +== APP - conversation == Input sent: What is dapr? +== APP - conversation == Output response: What is dapr? +``` + + + +2. Stop and clean up application processes. + + + +```bash +dapr stop -f . +``` + + + +## Run the app individually + +1. Open a terminal and run the `conversation` app. Build the dependencies if you haven't already. + +```bash +cd ./conversation +dotnet build +``` + +2. Run the Dapr process alongside the application. + +```bash +dapr run --app-id conversation --resources-path ../../../components/ -- dotnet run +``` + +The terminal console output should look similar to this, where: + +- The app sends an input `What is dapr?` to the `echo` Component mock LLM. +- The mock LLM echoes `What is dapr?`. + +```text +== APP - conversation == Input sent: What is dapr? +== APP - conversation == Output response: What is dapr? +``` diff --git a/conversation/csharp/sdk/conversation/Program.cs b/conversation/csharp/sdk/conversation/Program.cs new file mode 100644 index 000000000..c5562381a --- /dev/null +++ b/conversation/csharp/sdk/conversation/Program.cs @@ -0,0 +1,54 @@ +/* +Copyright 2024 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using Dapr.AI.Conversation; +using Dapr.AI.Conversation.Extensions; + +class Program +{ + private const string ConversationComponentName = "echo"; + + static async Task Main(string[] args) + { + const string prompt = "What is dapr?"; + + var builder = WebApplication.CreateBuilder(args); + builder.Services.AddDaprConversationClient(); + var app = builder.Build(); + + //Instantiate Dapr Conversation Client + var conversationClient = app.Services.GetRequiredService(); + + try + { + // Send a request to the echo mock LLM component + var response = await conversationClient.ConverseAsync(ConversationComponentName, [new(prompt, DaprConversationRole.Generic)]); + Console.WriteLine("Input sent: " + prompt); + + if (response != null) + { + Console.Write("Output response:"); + foreach (var resp in response.Outputs) + { + Console.WriteLine($" {resp.Result}"); + } + } + } + catch (Exception ex) + { + Console.WriteLine("Error: " + ex.Message); + } + } +} diff --git a/conversation/csharp/sdk/conversation/Program.csproj b/conversation/csharp/sdk/conversation/Program.csproj new file mode 100644 index 000000000..c641f3a3f --- /dev/null +++ b/conversation/csharp/sdk/conversation/Program.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + diff --git a/conversation/csharp/sdk/dapr.yaml b/conversation/csharp/sdk/dapr.yaml new file mode 100644 index 000000000..d9d9b265a --- /dev/null +++ b/conversation/csharp/sdk/dapr.yaml @@ -0,0 +1,8 @@ +version: 1 +common: + resourcesPath: ../../components/ +apps: + - appDirPath: ./conversation/ + appID: conversation + daprHTTPPort: 3500 + command: ["dotnet", "run"] \ No newline at end of file diff --git a/conversation/csharp/sdk/makefile b/conversation/csharp/sdk/makefile new file mode 100644 index 000000000..e7a8826bf --- /dev/null +++ b/conversation/csharp/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/conversation/go/http/README.md b/conversation/go/http/README.md new file mode 100644 index 000000000..36c81b23a --- /dev/null +++ b/conversation/go/http/README.md @@ -0,0 +1,61 @@ +# Dapr Conversation API (Go HTTP) + +In this quickstart, you'll send an input to a mock Large Language Model (LLM) using Dapr's Conversation API. This API is responsible for providing one consistent API entry point to talk to underlying LLM providers. + +Visit [this](https://v1-15.docs.dapr.io/developing-applications/building-blocks/conversation/conversation-overview/) link for more information about Dapr and the Conversation API. + +> **Note:** This example leverages HTTP `requests` only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/). + +This quickstart includes one app: + +- `conversation.go`, responsible for sending an input to the underlying LLM and retrieving an output. + +## Run the app with the template file + +This section shows how to run the application using the [multi-app run template files](https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-overview/) with `dapr run -f .`. + +This example uses the default LLM Component provided by Dapr which simply echoes the input provided, for testing purposes. Here are other [supported Conversation components](https://v1-15.docs.dapr.io/reference/components-reference/supported-conversation/). + +Open a new terminal window and run the multi app run template: + + + +```bash +dapr run -f . +``` + +The terminal console output should look similar to this, where: + +- The app sends an input `What is dapr?` to the `echo` Component mock LLM. +- The mock LLM echoes `What is dapr?`. + +```text +== APP - conversation == Input sent: What is dapr? +== APP - conversation == Output response: What is dapr? +``` + + + +2. Stop and clean up application processes. + + + +```bash +dapr stop -f . +``` + + \ No newline at end of file diff --git a/conversation/go/http/conversation/conversation.go b/conversation/go/http/conversation/conversation.go new file mode 100644 index 000000000..0d76cbae4 --- /dev/null +++ b/conversation/go/http/conversation/conversation.go @@ -0,0 +1,84 @@ +/* +Copyright 2024 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import ( + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "os" + "strings" + "time" +) + +const conversationComponentName = "echo" + +func main() { + daprHost := os.Getenv("DAPR_HOST") + if daprHost == "" { + daprHost = "http://localhost" + } + daprHttpPort := os.Getenv("DAPR_HTTP_PORT") + if daprHttpPort == "" { + daprHttpPort = "3500" + } + + client := http.Client{ + Timeout: 15 * time.Second, + } + + var inputBody = `{ + "name": "echo", + "inputs": [{"message":"What is dapr?"}], + "parameters": {}, + "metadata": {} + }` + + reqURL := daprHost + ":" + daprHttpPort + "/v1.0-alpha1/conversation/" + conversationComponentName + "/converse" + + req, err := http.NewRequest("POST", reqURL, strings.NewReader(inputBody)) + if err != nil { + log.Fatal(err.Error()) + } + + req.Header.Set("Content-Type", "application/json") + + // Send a request to the echo LLM component + res, err := client.Do(req) + if err != nil { + log.Fatal(err) + } + + defer res.Body.Close() + + fmt.Println("Input sent: What is dapr?") + + bodyBytes, err := io.ReadAll(res.Body) + if err != nil { + log.Fatal(err) + } + + // Unmarshal the response + var data map[string][]map[string]string + if err := json.Unmarshal(bodyBytes, &data); err != nil { + log.Fatal(err) + } + + result := data["outputs"][0]["result"] + fmt.Println("Output response:", result) + +} diff --git a/conversation/go/http/conversation/go.mod b/conversation/go/http/conversation/go.mod new file mode 100644 index 000000000..0a6357435 --- /dev/null +++ b/conversation/go/http/conversation/go.mod @@ -0,0 +1,3 @@ +module conversation + +go 1.23.5 diff --git a/conversation/go/http/conversation/go.sum b/conversation/go/http/conversation/go.sum new file mode 100644 index 000000000..e69de29bb diff --git a/conversation/go/http/dapr.yaml b/conversation/go/http/dapr.yaml new file mode 100644 index 000000000..1187afdd2 --- /dev/null +++ b/conversation/go/http/dapr.yaml @@ -0,0 +1,8 @@ +version: 1 +common: + resourcesPath: ../../components/ +apps: + - appDirPath: ./conversation/ + appID: conversation + daprHTTPPort: 3501 + command: ["go", "run", "."] diff --git a/conversation/go/http/makefile b/conversation/go/http/makefile new file mode 100644 index 000000000..e7a8826bf --- /dev/null +++ b/conversation/go/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/conversation/go/sdk/README.md b/conversation/go/sdk/README.md new file mode 100644 index 000000000..3c8a3ad41 --- /dev/null +++ b/conversation/go/sdk/README.md @@ -0,0 +1,61 @@ +# Dapr Conversation API (Go SDK) + +In this quickstart, you'll send an input to a mock Large Language Model (LLM) using Dapr's Conversation API. This API is responsible for providing one consistent API entry point to talk to underlying LLM providers. + +Visit [this](https://v1-15.docs.dapr.io/developing-applications/building-blocks/conversation/conversation-overview/) link for more information about Dapr and the Conversation API. + +> **Note:** This example leverages the Dapr SDK. If you are looking for the example using the HTTP API [click here](../http/). + +This quickstart includes one app: + +- `conversation.go`, responsible for sending an input to the underlying LLM and retrieving an output. + +## Run the app with the template file + +This section shows how to run the application using the [multi-app run template files](https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-overview/) with `dapr run -f .`. + +This example uses the default LLM Component provided by Dapr which simply echoes the input provided, for testing purposes. Here are other [supported Conversation components](https://v1-15.docs.dapr.io/reference/components-reference/supported-conversation/). + +Open a new terminal window and run the multi app run template: + + + +```bash +dapr run -f . +``` + +The terminal console output should look similar to this, where: + +- The app sends an input `What is dapr?` to the `echo` Component mock LLM. +- The mock LLM echoes `What is dapr?`. + +```text +== APP - conversation == Input sent: What is dapr? +== APP - conversation == Output response: What is dapr? +``` + + + +2. Stop and clean up application processes. + + + +```bash +dapr stop -f . +``` + + \ No newline at end of file diff --git a/conversation/go/sdk/conversation/conversation.go b/conversation/go/sdk/conversation/conversation.go new file mode 100644 index 000000000..ff2d7bb4a --- /dev/null +++ b/conversation/go/sdk/conversation/conversation.go @@ -0,0 +1,49 @@ +/* +Copyright 2024 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import ( + "context" + "fmt" + "log" + + dapr "github.com/dapr/go-sdk/client" +) + +func main() { + client, err := dapr.NewClient() + if err != nil { + panic(err) + } + + input := dapr.ConversationInput{ + Message: "What is dapr?", + // Role: nil, // Optional + // ScrubPII: nil, // Optional + } + + fmt.Println("Input sent:", input.Message) + + var conversationComponent = "echo" + + request := dapr.NewConversationRequest(conversationComponent, []dapr.ConversationInput{input}) + + resp, err := client.ConverseAlpha1(context.Background(), request) + if err != nil { + log.Fatalf("err: %v", err) + } + + fmt.Println("Output response:", resp.Outputs[0].Result) +} diff --git a/conversation/go/sdk/conversation/go.mod b/conversation/go/sdk/conversation/go.mod new file mode 100644 index 000000000..9fd0d64bb --- /dev/null +++ b/conversation/go/sdk/conversation/go.mod @@ -0,0 +1,19 @@ +module conversation + +go 1.23.5 + +require github.com/dapr/go-sdk v1.12.0-rc.1 + +require ( + github.com/dapr/dapr v1.15.0-rc.1 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/kr/pretty v0.3.1 // indirect + go.opentelemetry.io/otel v1.32.0 // indirect + golang.org/x/net v0.32.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect + google.golang.org/grpc v1.68.1 // indirect + google.golang.org/protobuf v1.35.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/conversation/go/sdk/conversation/go.sum b/conversation/go/sdk/conversation/go.sum new file mode 100644 index 000000000..3f4a31e7c --- /dev/null +++ b/conversation/go/sdk/conversation/go.sum @@ -0,0 +1,45 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/dapr v1.15.0-rc.1 h1:7JP3zSannxQwV27A9pPR2b/DSNmgcSjJOhRDwM4eFpQ= +github.com/dapr/dapr v1.15.0-rc.1/go.mod h1:SycZrBWgfmog+C5T4p0X6VIpnREQ3xajrYxdih+gn9w= +github.com/dapr/go-sdk v1.12.0-rc.1 h1:KK92BahLmwGowVRjFxsjySl25M6wwuJSjesYIIF6h0c= +github.com/dapr/go-sdk v1.12.0-rc.1/go.mod h1:OxCF7Eh8IZvmNv6Euk+mnLrehJyLQRYb4TAU7uHq7Ow= +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/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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/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/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +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.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= +google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +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= diff --git a/conversation/go/sdk/dapr.yaml b/conversation/go/sdk/dapr.yaml new file mode 100644 index 000000000..6a6c0e0fc --- /dev/null +++ b/conversation/go/sdk/dapr.yaml @@ -0,0 +1,7 @@ +version: 1 +common: + resourcesPath: ../../components/ +apps: + - appDirPath: ./conversation/ + appID: conversation + command: ["go", "run", "."] diff --git a/conversation/go/sdk/makefile b/conversation/go/sdk/makefile new file mode 100644 index 000000000..e7a8826bf --- /dev/null +++ b/conversation/go/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/jobs/csharp/http/README.md b/jobs/csharp/http/README.md new file mode 100644 index 000000000..4d4370bae --- /dev/null +++ b/jobs/csharp/http/README.md @@ -0,0 +1,181 @@ +# Dapr Jobs API (HTTP Client) + +In this quickstart, you'll schedule, get, and delete a job using Dapr's Job API. This API is responsible for scheduling and running jobs at a specific time or interval. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/jobs/) link for more information about Dapr and the Jobs API. + +> **Note:** This example leverages HTTP requests only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/). + +This quickstart includes two apps: + +- Jobs Scheduler, responsible for scheduling, retrieving and deleting jobs. +- Jobs Service, responsible for handling the triggered jobs. + +## Run all apps with multi-app run template file + +This section shows how to run both applications at once using [multi-app run template files](https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-overview/) with `dapr run -f .`. This enables to you test the interactions between multiple applications and will `schedule`, `run`, `get`, and `delete` jobs within a single process. + +1. Build the apps: + + + +```bash +cd ./job-service +dotnet build +``` + + + + + +```bash +cd ./job-scheduler +dotnet build +``` + + + +2. Run the multi app run template: + + + +```bash +dapr run -f . +``` + +The terminal console output should look similar to this, where: + +- The `R2-D2` job is being scheduled. +- The `R2-D2` job is being retrieved. +- The `C-3PO` job is being scheduled. +- The `C-3PO` job is being retrieved. +- The `R2-D2` job is being executed after 15 seconds. +- The `C-3PO` job is being executed after 20 seconds. + +```text +== APP - job-scheduler == Job Scheduled: R2-D2 +== APP - job-scheduler == Job details: {"name":"R2-D2", "dueTime":"15s", "data":{"@type":"type.googleapis.com/google.protobuf.Value", "value":{"Value":"R2-D2:Oil Change"}}} +== APP - job-scheduler == Job Scheduled: C-3PO +== APP - job-scheduler == Job details: {"name":"C-3PO", "dueTime":"20s", "data":{"@type":"type.googleapis.com/google.protobuf.Value", "value":{"Value":"C-3PO:Limb Calibration"}}} +== APP - job-service == Received job request... +== APP - job-service == Starting droid: R2-D2 +== APP - job-service == Executing maintenance job: Oil Change +``` + +After 20 seconds, the terminal output should present the `C-3PO` job being processed: + +```text +== APP - job-service == Received job request... +== APP - job-service == Starting droid: C-3PO +== APP - job-service == Executing maintenance job: Limb Calibration +``` + + + +## Run apps individually + +### Schedule Jobs + +1. Open a terminal and run the `job-service` app. Build the dependencies if you haven't already. + +```bash +cd ./job-service +dotnet build +``` + +```bash +dapr run --app-id job-service --app-port 6200 --dapr-http-port 6280 -- dotnet run +``` + +2. In a new terminal window, schedule the `R2-D2` Job using the Jobs API. + +```bash +curl -X POST \ + http://localhost:6280/v1.0-alpha1/jobs/r2-d2 \ + -H "Content-Type: application/json" \ + -d '{ + "data": { + "Value": "R2-D2:Oil Change" + }, + "dueTime": "2s" + }' +``` + +In the `job-service` terminal window, the output should be: + +```text +== APP - job-app == Received job request... +== APP - job-app == Starting droid: R2-D2 +== APP - job-app == Executing maintenance job: Oil Change +``` + +3. On the same terminal window, schedule the `C-3PO` Job using the Jobs API. + +```bash +curl -X POST \ + http://localhost:6280/v1.0-alpha1/jobs/c-3po \ + -H "Content-Type: application/json" \ + -d '{ + "data": { + "Value": "C-3PO:Limb Calibration" + }, + "dueTime": "30s" + }' +``` + +### Get a scheduled job + +1. On the same terminal window, run the command below to get the recently scheduled `C-3PO` job. + +```bash +curl -X GET http://localhost:6280/v1.0-alpha1/jobs/c-3po -H "Content-Type: application/json" +``` + +You should see the following: + +```text +{"name":"c-3po", "dueTime":"30s", "data":{"@type":"type.googleapis.com/google.protobuf.Value", "value":{"Value":"C-3PO:Limb Calibration"}} +``` + +### Delete a scheduled job + +1. On the same terminal window, run the command below to deleted the recently scheduled `C-3PO` job. + +```bash +curl -X DELETE http://localhost:6280/v1.0-alpha1/jobs/c-3po -H "Content-Type: application/json" +``` + +2. Run the command below to attempt to retrieve the deleted job: + +```bash +curl -X GET http://localhost:6280/v1.0-alpha1/jobs/c-3po -H "Content-Type: application/json" +``` + +In the `job-service` terminal window, the output should be similar to the following: + +```text +ERRO[0568] Error getting job c-3po due to: rpc error: code = Unknown desc = job not found: c-3po instance=local scope=dapr.api type=log ver=1.15.0 +``` diff --git a/jobs/csharp/http/dapr.yaml b/jobs/csharp/http/dapr.yaml new file mode 100644 index 000000000..f52271b71 --- /dev/null +++ b/jobs/csharp/http/dapr.yaml @@ -0,0 +1,13 @@ +version: 1 +apps: + - appDirPath: ./job-service/ + appID: job-service + appPort: 6200 + daprHTTPPort: 6280 + schedulerHostAddress: localhost + command: ["dotnet", "run"] + - appDirPath: ./job-scheduler/ + appID: job-scheduler + appPort: 6300 + daprHTTPPort: 6380 + command: ["dotnet", "run"] \ No newline at end of file diff --git a/jobs/csharp/http/job-scheduler/Program.cs b/jobs/csharp/http/job-scheduler/Program.cs new file mode 100644 index 000000000..86697f357 --- /dev/null +++ b/jobs/csharp/http/job-scheduler/Program.cs @@ -0,0 +1,78 @@ +using System; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +// Job request bodies +var c3poJobBody = new +{ + data = new { Value = "C-3PO:Limb Calibration" }, + dueTime = "20s" +}; + +var r2d2JobBody = new +{ + data = new { Value = "R2-D2:Oil Change" }, + dueTime = "15s" +}; + +var daprHost = Environment.GetEnvironmentVariable("DAPR_HOST") ?? "http://localhost"; +var schedulerDaprHttpPort = "6280"; + +var httpClient = new HttpClient(); + +await Task.Delay(5000); // Wait for job-service to start + +try +{ + // Schedule R2-D2 job + await ScheduleJob("R2-D2", r2d2JobBody); + await Task.Delay(5000); + // Get R2-D2 job details + await GetJobDetails("R2-D2"); + + // Schedule C-3PO job + await ScheduleJob("C-3PO", c3poJobBody); + await Task.Delay(5000); + // Get C-3PO job details + await GetJobDetails("C-3PO"); + + await Task.Delay(30000); // Allow time for jobs to complete +} +catch (Exception ex) +{ + Console.Error.WriteLine($"Error: {ex.Message}"); + Environment.Exit(1); +} + +async Task ScheduleJob(string jobName, object jobBody) +{ + var reqURL = $"{daprHost}:{schedulerDaprHttpPort}/v1.0-alpha1/jobs/{jobName}"; + var jsonBody = JsonSerializer.Serialize(jobBody); + var content = new StringContent(jsonBody, Encoding.UTF8, "application/json"); + + var response = await httpClient.PostAsync(reqURL, content); + + if (response.StatusCode != System.Net.HttpStatusCode.NoContent) + { + throw new Exception($"Failed to register job event handler. Status code: {response.StatusCode}"); + } + + Console.WriteLine($"Job Scheduled: {jobName}"); +} + +async Task GetJobDetails(string jobName) +{ + var reqURL = $"{daprHost}:{schedulerDaprHttpPort}/v1.0-alpha1/jobs/{jobName}"; + + var response = await httpClient.GetAsync(reqURL); + + if (!response.IsSuccessStatusCode) + { + throw new Exception($"HTTP error! Status: {response.StatusCode}"); + } + + var jobDetails = await response.Content.ReadAsStringAsync(); + Console.WriteLine($"Job details: {jobDetails}"); +} diff --git a/jobs/csharp/http/job-scheduler/jobs-scheduler.csproj b/jobs/csharp/http/job-scheduler/jobs-scheduler.csproj new file mode 100644 index 000000000..9a93a9ffc --- /dev/null +++ b/jobs/csharp/http/job-scheduler/jobs-scheduler.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + jobs_scheduler + enable + enable + + + diff --git a/jobs/csharp/http/job-service/Program.cs b/jobs/csharp/http/job-service/Program.cs new file mode 100644 index 000000000..e016dbc5c --- /dev/null +++ b/jobs/csharp/http/job-service/Program.cs @@ -0,0 +1,75 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http.Json; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System.Text.Json; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.Configure(options => +{ + options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; +}); + +var app = builder.Build(); +var appPort = Environment.GetEnvironmentVariable("APP_PORT") ?? "6200"; + +//Job handler route +app.MapPost("/job/{*path}", async (HttpRequest request, HttpResponse response) => +{ + Console.WriteLine("Received job request..."); + + try + { + // Parse the incoming JSON body + var jobData = await JsonSerializer.DeserializeAsync(request.Body); + if (jobData == null || string.IsNullOrEmpty(jobData.Value)) + { + throw new Exception("Invalid job data. 'value' field is required."); + } + + // Creating Droid Job from decoded value + var droidJob = SetDroidJob(jobData.Value); + Console.WriteLine($"Starting droid: {droidJob.Droid}"); + Console.WriteLine($"Executing maintenance job: {droidJob.Task}"); + response.StatusCode = 200; + } + catch (Exception ex) + { + Console.Error.WriteLine($"Error processing job: {ex.Message}"); + response.StatusCode = 400; // Bad Request + var errorResponse = new { error = $"Error processing request: {ex.Message}" }; + await response.WriteAsJsonAsync(errorResponse); + } +}); + +// Start the server +app.Run($"http://localhost:{appPort}"); + +static DroidJob SetDroidJob(string droidStr) +{ + var parts = droidStr.Split(":"); + if (parts.Length != 2) + { + throw new Exception("Invalid droid job format. Expected format: 'Droid:Task'"); + } + + return new DroidJob + { + Droid = parts[0], + Task = parts[1] + }; +} + +// Classes for request and response models +public class JobData +{ + public string? Value { get; set; } +} + +public class DroidJob +{ + public string? Droid { get; set; } + public string? Task { get; set; } +} diff --git a/jobs/csharp/http/job-service/Properties/launchSettings.json b/jobs/csharp/http/job-service/Properties/launchSettings.json new file mode 100644 index 000000000..22eea4023 --- /dev/null +++ b/jobs/csharp/http/job-service/Properties/launchSettings.json @@ -0,0 +1,38 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:5305", + "sslPort": 44346 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5023", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7073;http://localhost:5023", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/jobs/csharp/http/job-service/appsettings.Development.json b/jobs/csharp/http/job-service/appsettings.Development.json new file mode 100644 index 000000000..ff66ba6b2 --- /dev/null +++ b/jobs/csharp/http/job-service/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/jobs/csharp/http/job-service/appsettings.json b/jobs/csharp/http/job-service/appsettings.json new file mode 100644 index 000000000..4d566948d --- /dev/null +++ b/jobs/csharp/http/job-service/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/jobs/csharp/http/job-service/job-service.csproj b/jobs/csharp/http/job-service/job-service.csproj new file mode 100644 index 000000000..b953f863d --- /dev/null +++ b/jobs/csharp/http/job-service/job-service.csproj @@ -0,0 +1,10 @@ + + + + net8.0 + enable + enable + job_service + + + diff --git a/jobs/csharp/http/makefile b/jobs/csharp/http/makefile new file mode 100644 index 000000000..e7a8826bf --- /dev/null +++ b/jobs/csharp/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/jobs/go/http/README.md b/jobs/go/http/README.md index 67233bab8..7c620f3f8 100644 --- a/jobs/go/http/README.md +++ b/jobs/go/http/README.md @@ -2,7 +2,7 @@ In this quickstart, you'll schedule, get, and delete a job using Dapr's Job API. This API is responsible for scheduling and running jobs at a specific time or interval. -Visit [this](https://v1-14.docs.dapr.io/developing-applications/building-blocks/jobs/) link for more information about Dapr and the Jobs API. +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/jobs/) link for more information about Dapr and the Jobs API. > **Note:** This example leverages HTTP `requests` only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/). @@ -11,7 +11,7 @@ This quickstart includes two apps: - `job-scheduler.go`, responsible for scheduling, retrieving and deleting jobs. - `job-service.go`, responsible for handling the triggered jobs. -## Run the app with the template file +## Run all apps with multi-app run template file This section shows how to run both applications at once using [multi-app run template files](https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-overview/) with `dapr run -f .`. This enables to you test the interactions between multiple applications and will `schedule`, `run`, `get`, and `delete` jobs within a single process. @@ -76,7 +76,7 @@ dapr stop -f . -## Run the Jobs APIs individually +## Run apps individually ### Schedule Jobs diff --git a/jobs/go/sdk/README.md b/jobs/go/sdk/README.md index 0938ebd7e..8f8c7468d 100644 --- a/jobs/go/sdk/README.md +++ b/jobs/go/sdk/README.md @@ -2,7 +2,7 @@ In this quickstart, you'll schedule, get, and delete a job using Dapr's Job API. This API is responsible for scheduling and running jobs at a specific time or interval. -Visit [this](https://v1-14.docs.dapr.io/developing-applications/building-blocks/jobs/) link for more information about Dapr and the Job API. +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/jobs/) link for more information about Dapr and the Job API. > **Note:** This example leverages the SDK only. If you are looking for the example using the HTTP requests [click here](../http/). diff --git a/pub_sub/python/sdk/order-processor-fastapi/requirements.txt b/pub_sub/python/sdk/order-processor-fastapi/requirements.txt index 21d943187..237ae13f3 100644 --- a/pub_sub/python/sdk/order-processor-fastapi/requirements.txt +++ b/pub_sub/python/sdk/order-processor-fastapi/requirements.txt @@ -2,3 +2,4 @@ fastapi dapr-ext-fastapi-dev cloudevents uvicorn +anyio>=4.4.0 # not directly required, pinned by Snyk to avoid a vulnerability diff --git a/tutorials/hello-world/.vscode/launch.json b/tutorials/hello-world/.vscode/launch.json index d25274624..caaeea234 100644 --- a/tutorials/hello-world/.vscode/launch.json +++ b/tutorials/hello-world/.vscode/launch.json @@ -13,7 +13,8 @@ ], "program": "${workspaceFolder}/node/app.js", "preLaunchTask": "daprd-debug-node", - "postDebugTask": "daprd-down-node" + "postDebugTask": "daprd-down-node", + "console": "integratedTerminal" }, { "type": "python", diff --git a/workflows/csharp/sdk/README.md b/workflows/csharp/sdk/README.md index bd96dfd71..635afb0d9 100644 --- a/workflows/csharp/sdk/README.md +++ b/workflows/csharp/sdk/README.md @@ -28,12 +28,13 @@ dotnet build ``` -2. Run the console app with Dapr: +2. Run the console app with Dapr. Note that you may need to run `cd ..` to move the working directory up one to +'/workflows/csharp/sdk':