diff --git a/COMPONENTS.md b/COMPONENTS.md index 095b3b4af3..179651550b 100644 --- a/COMPONENTS.md +++ b/COMPONENTS.md @@ -153,7 +153,7 @@ For example, the Python SDK integrations page pre-filters to Python: ``` -Valid SDK values are `"Java"`, `"Python"`, `"TypeScript"`, and `"Ruby"`. +Valid SDK values are `"Go"`, `"Java"`, `"Python"`, `"TypeScript"`, and `"Ruby"`. ### Filter behavior diff --git a/docs/develop/go/index.mdx b/docs/develop/go/index.mdx index 62c05b03d0..d4070f9257 100644 --- a/docs/develop/go/index.mdx +++ b/docs/develop/go/index.mdx @@ -93,7 +93,11 @@ From there, you can dive deeper into any of the Temporal primitives to start bui - [Testing](/develop/go/best-practices/testing-suite) - [Data handling](/develop/go/data-handling) -## Temporal Go Technical Resources +## [Integrations](/develop/go/integrations) + +- [Google ADK integration](/develop/go/integrations/google-adk) + +## Temporal Go technical resources - [Go SDK Quickstart - Setup Guide](/develop/go/set-up-your-local-go) - [Go API Documentation](https://pkg.go.dev/go.temporal.io/sdk) diff --git a/docs/develop/go/integrations/google-adk.mdx b/docs/develop/go/integrations/google-adk.mdx new file mode 100644 index 0000000000..cee828b798 --- /dev/null +++ b/docs/develop/go/integrations/google-adk.mdx @@ -0,0 +1,612 @@ +--- +id: google-adk +title: Google ADK integration +sidebar_label: Google ADK +toc_max_heading_level: 2 +keywords: + - ai + - agents + - google + - adk + - durable execution + - ai workflows +tags: + - Google ADK + - Go SDK + - Temporal SDKs +description: Run Google ADK agents with durable execution using the Temporal Go SDK and the googleadk contrib integration. +--- + +Temporal's integration with [Google ADK](https://google.github.io/adk-docs/) (`adk-go`) gives your agents +[Durable Execution](/temporal#durable-execution): the agent's orchestration loop runs inside a Temporal Workflow, each +LLM call becomes a durable Temporal Activity, and any tool that does I/O runs as an Activity too — so every step is +retried, timed out, recorded in Workflow history, and replayable after crashes or restarts. + +You keep building agents the native ADK way — `llmagent.New(...)` with a `model.LLM`, `tool.Tool`s / `tool.Toolset`s and +`SubAgents`, wrapped in `runner.New(...)` and driven by `r.Run(...)`. You change two things: + +1. Use `googleadk.NewModel("")` as your agent's `Model`. It is a `model.LLM` whose calls dispatch to the + `InvokeModel` Activity; the real model is reconstructed **worker-side**, never in the Workflow. +2. Pass `googleadk.NewContext(workflowCtx)` to `r.Run`, which installs Temporal-deterministic time, UUID, and + task-fan-out providers so the agent loop replays deterministically. + +Tools run **in-workflow by default** (the idiomatic Temporal model: the Workflow is deterministic, and anything touching +the network, clock, or disk goes through an Activity). Opt a tool into an Activity with `googleadk.ActivityAsTool`, or +use `googleadk.NewMCPToolset` for MCP. + +:::info + +The `googleadk` contrib module is new. It depends on determinism seams (`platform.WithTimeProvider`, +`WithUUIDProvider`, `WithTaskRunner`) that merged into `google.golang.org/adk/v2` after its latest tagged release, so +`go.mod` pins `adk/v2` to a `main`-branch pseudo-version until a release ships that includes them. + +::: + +Code snippets in this guide are taken from the +[Google ADK plugin samples](https://github.com/temporalio/samples-go/tree/main/googleadk). Refer to the samples for the +complete, runnable code. + +## Prerequisites + +- This guide assumes you are already familiar with Google ADK. If you aren't, refer to the + [Google ADK documentation](https://google.github.io/adk-docs/) for more details. +- If you are new to Temporal, read [Understanding Temporal](/evaluate/understanding-temporal) or take the + [Temporal 101](https://learn.temporal.io/courses/temporal_101/) course. +- Set up your local development environment by following the + [Set up your local development environment](/develop/go/set-up-your-local-go) guide. Leave the Temporal development + server running if you want to test your code locally. +- Provide model credentials **worker-side** — for Gemini, set `GEMINI_API_KEY` (or `GOOGLE_API_KEY`) in the worker's + environment. Credentials are captured in the worker's `ModelFactory` and never cross the Activity boundary into the + Workflow. + +## Install the plugin + +Install the `googleadk` contrib module: + +```bash +go get go.temporal.io/sdk/contrib/googleadk@latest +``` + +```go +import "go.temporal.io/sdk/contrib/googleadk" +``` + +## Run an agent with Durable Execution + +An agent has two halves: the **worker** registers the real model behind the `InvokeModel` Activity, and the **workflow** +builds a vanilla ADK agent and drives it. + +### Configure the Worker + +Build the worker-side registry with `googleadk.NewActivities` and register it. The real Gemini model lives here, behind +the Activity boundary; the API key is read worker-side. Disable the model SDK's own retries so Temporal's `RetryPolicy` +is the single source of truth. + + +[googleadk/worker/main.go](https://github.com/temporalio/samples-go/blob/main/googleadk/worker/main.go) +```go +w := worker.New(c, adk.TaskQueue, worker.Options{}) + +w.RegisterWorkflow(adk.AgentWorkflow) +// Register GetWeather under the tool name the ActivityAsTool dispatches, so the +// agent's get_weather call resolves to this activity. +w.RegisterActivityWithOptions(adk.GetWeather, activity.RegisterOptions{Name: adk.WeatherToolName}) + +// Register the integration's model Activity. The real Gemini model lives here, +// behind the Activity boundary; the API key is read worker-side from the env +// and never crosses into the workflow. Disable the model SDK's own retries so +// Temporal's RetryPolicy is the single source of truth. +acts, err := googleadk.NewActivities(googleadk.Config{ + Models: map[string]googleadk.ModelFactory{ + adk.ModelName: func(ctx context.Context, name string) (model.LLM, error) { + // nil config reads GEMINI_API_KEY / GOOGLE_API_KEY from the env. + return gemini.NewModel(ctx, name, nil) + }, + }, +}) +if err != nil { + log.Fatalln("Unable to build googleadk activities", err) +} +acts.Register(w) + +if err := w.Run(worker.InterruptCh()); err != nil { + log.Fatalln("Unable to start worker", err) +} +``` + + +`Config.Models` is optional for providers ADK's registry already knows (for example `gemini-*`): when a model name is +absent, `InvokeModel` falls back to `model.NewLLM`. Supply a factory to inject credentials, disable the model SDK's own +retries, or override the default. + +### Define the Workflow + +Build the agent the ordinary ADK way, using `googleadk.NewModel` for the model and passing `googleadk.NewContext(ctx)` +to `r.Run`. The `get_weather` tool is an ordinary Temporal Activity exposed to the agent with `googleadk.ActivityAsTool`. + + +[googleadk/workflow.go](https://github.com/temporalio/samples-go/blob/main/googleadk/workflow.go) +```go +func AgentWorkflow(ctx workflow.Context, question string) (string, error) { + weatherTool, err := googleadk.ActivityAsTool(GetWeather, googleadk.ActivityToolOptions{ + Name: WeatherToolName, + Description: "Get the current weather for a city.", + }) + if err != nil { + return "", err + } + + // Build the agent the ordinary ADK way. NewModel is a model.LLM that carries + // only the model name in-workflow; the real Gemini client lives worker-side. + root, err := llmagent.New(llmagent.Config{ + Name: "assistant", + Description: "a helpful weather assistant", + Model: googleadk.NewModel(ModelName), + Instruction: "Answer the user's question. Use the get_weather tool when asked about the weather.", + Tools: []tool.Tool{weatherTool}, + }) + if err != nil { + return "", err + } + + r, err := runner.New(runner.Config{ + AppName: "weather", + Agent: root, + SessionService: session.InMemoryService(), + AutoCreateSession: true, + }) + if err != nil { + return "", err + } + + // NewContext bridges the workflow.Context into the context ADK reads its + // determinism/executor seams from. Pass it straight to Run. + adkCtx := googleadk.NewContext(ctx) + msg := genai.NewContentFromText(question, genai.RoleUser) + + var answer string + for ev, err := range r.Run(adkCtx, "user-1", "session-1", msg, agent.RunConfig{}) { + if err != nil { + return "", err + } + if ev != nil && ev.Content != nil { + for _, p := range ev.Content.Parts { + if p != nil && p.Text != "" { + answer = p.Text + } + } + } + } + return answer, nil +} + +``` + + +The tool itself is an ordinary Temporal Activity — register it on the worker as usual, and expose it to the agent with +`ActivityAsTool` (its parameter schema is inferred from the argument type): + + +[googleadk/workflow.go](https://github.com/temporalio/samples-go/blob/main/googleadk/workflow.go) +```go +func GetWeather(ctx context.Context, in GetWeatherInput) (GetWeatherOutput, error) { + return GetWeatherOutput{City: in.City, Conditions: "sunny, 72°F"}, nil +} + +``` + + +### Start the Workflow + +Start the Workflow like any other and read its result: + + +[googleadk/starter/main.go](https://github.com/temporalio/samples-go/blob/main/googleadk/starter/main.go) +```go +question := "What's the weather in San Francisco?" +we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, adk.AgentWorkflow, question) +if err != nil { + log.Fatalln("Unable to execute workflow", err) +} + +log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) + +// Synchronously wait for the workflow completion. +var answer string +if err := we.Get(context.Background(), &answer); err != nil { + log.Fatalln("Unable to get workflow result", err) +} +log.Println("Agent answer:", answer) +``` + + +## Tools + +- **Function tools run in-workflow by default.** Ordinary `functiontool.New(...)` tools run on Temporal's deterministic + dispatcher inside the Workflow — no Activity overhead — and their session-state mutations propagate normally. Their + code must be deterministic and replay-safe: no direct network, clock, randomness, or goroutines. +- **Opt a tool into an Activity when it does I/O.** `googleadk.ActivityAsTool(myActivity, ...)` exposes an existing + `func(context.Context, TArgs) (TResults, error)` Temporal Activity to the agent as a tool (shown in the Hello World + Workflow above); its call dispatches the Activity, so it is retried, timed out, and visible in the UI. +- **MCP, statelessly.** `googleadk.NewMCPToolset(...)` is a workflow-side proxy that lists remote tools via the + `ListMcpTools` Activity and executes calls via `CallMcpTool`. The live, stateful `mcptoolset.New(...)` runs worker-side + (registered in `Config.MCPToolsets`), never in the Workflow. + +## Multi-agent systems + +Build a coordinator agent with specialist `SubAgents`; ADK wires the parent/child relationship and exposes the built-in +`transfer_to_agent` tool automatically. The entire tree — including the transfer hop — runs in the Workflow; only the +model calls and any Activity-backed tools leave it. + + +[googleadk/multiagent/workflow.go](https://github.com/temporalio/samples-go/blob/main/googleadk/multiagent/workflow.go) +```go +func MultiAgentWorkflow(ctx workflow.Context, question string) (string, error) { + weatherTool, err := googleadk.ActivityAsTool(GetWeather, googleadk.ActivityToolOptions{ + Name: WeatherToolName, + Description: "Get the current weather for a city.", + }) + if err != nil { + return "", err + } + + // The weather specialist owns the get_weather tool. + weather, err := llmagent.New(llmagent.Config{ + Name: "weather", + Description: "answers questions about the current weather in a city", + Model: googleadk.NewModel(WeatherModelName), + Instruction: "You are a weather specialist. Use the get_weather tool to answer weather questions.", + Tools: []tool.Tool{weatherTool}, + }) + if err != nil { + return "", err + } + + // The jokes specialist just tells jokes. + jokes, err := llmagent.New(llmagent.Config{ + Name: "jokes", + Description: "tells a light-hearted joke", + Model: googleadk.NewModel(JokesModelName), + Instruction: "You are a comedian. Respond with a short, friendly joke.", + }) + if err != nil { + return "", err + } + + // The coordinator delegates to whichever specialist fits the question. ADK + // wires the parent/child relationship from SubAgents and exposes the built-in + // transfer_to_agent tool automatically. + coordinator, err := llmagent.New(llmagent.Config{ + Name: "coordinator", + Description: "routes the user's request to the right specialist", + Model: googleadk.NewModel(CoordinatorModelName), + Instruction: "You are a router. Delegate weather questions to the weather agent " + + "and requests for a joke to the jokes agent. Do not answer directly.", + SubAgents: []agent.Agent{weather, jokes}, + }) + if err != nil { + return "", err + } + + r, err := runner.New(runner.Config{ + AppName: "multiagent", + Agent: coordinator, + SessionService: session.InMemoryService(), + AutoCreateSession: true, + }) + if err != nil { + return "", err + } + + adkCtx := googleadk.NewContext(ctx) + msg := genai.NewContentFromText(question, genai.RoleUser) + + var answer string + for ev, err := range r.Run(adkCtx, "user-1", "session-1", msg, agent.RunConfig{}) { + if err != nil { + return "", err + } + if ev == nil || ev.Content == nil { + continue + } + // Keep the last non-empty text produced by any agent in the tree; after a + // transfer_to_agent hop this is the specialist's answer. + for _, p := range ev.Content.Parts { + if p != nil && p.Text != "" { + answer = p.Text + } + } + } + return answer, nil +} + +``` + + +## Human-in-the-loop tool confirmation + +A sensitive tool calls ADK's `ctx.RequestConfirmation(hint, payload)`, which ends the turn with an +`adk_request_confirmation` function call. The Workflow detects pending confirmations with +`googleadk.PendingConfirmations`, durably waits for the human's decision (delivered as a Temporal signal), and resumes +the agent with `googleadk.ConfirmationResponse`. Because the wait is durable, the Workflow can sit idle for days and +survive worker restarts — when the approval signal arrives, the agent resumes exactly where it paused. + + +[googleadk/humanintheloop/workflow.go](https://github.com/temporalio/samples-go/blob/main/googleadk/humanintheloop/workflow.go) +```go +func ApprovalWorkflow(ctx workflow.Context, request string) (Result, error) { + delTool, err := functiontool.New[DeleteArgs, map[string]any]( + functiontool.Config{ + Name: DeleteToolName, + Description: "Delete a named resource. Requires human confirmation before it runs.", + }, + deleteResource, + ) + if err != nil { + return Result{}, err + } + + root, err := llmagent.New(llmagent.Config{ + Name: "assistant", + Description: "an assistant that can delete resources with human approval", + Model: googleadk.NewModel(ModelName), + Instruction: "Use the delete_resource tool when the user asks to delete something.", + Tools: []tool.Tool{delTool}, + }) + if err != nil { + return Result{}, err + } + + r, err := runner.New(runner.Config{ + AppName: "hitl", + Agent: root, + SessionService: session.InMemoryService(), + AutoCreateSession: true, + }) + if err != nil { + return Result{}, err + } + + adkCtx := googleadk.NewContext(ctx) + msg := genai.NewContentFromText(request, genai.RoleUser) + + var res Result + // Drive the run in passes: each Run call is one pass over the same session. A + // pass either completes (no pending confirmation) or pauses awaiting a human. + for { + var events []*session.Event + for ev, err := range r.Run(adkCtx, "user-1", "session-1", msg, agent.RunConfig{}) { + if err != nil { + return Result{}, err + } + if ev == nil { + continue + } + events = append(events, ev) + if ev.Content != nil { + for _, p := range ev.Content.Parts { + if p != nil && p.Text != "" { + res.Answer = p.Text + } + } + } + } + + pending := googleadk.PendingConfirmations(events) + if len(pending) == 0 { + // The agent finished without (further) confirmations needed. + return res, nil + } + + // The agent paused. Durably wait for the human's decision to arrive as a + // Temporal signal. This is the whole point: the workflow can sit here for + // as long as it takes — across worker restarts — without losing state. + // + // This handles one pending confirmation per pass — the recommended + // pattern (see the googleadk.ConfirmationResponse docs): resuming + // several decisions at once can re-dispatch the approved tool calls in + // an order that is not replay-stable. Any other pending confirmations + // simply surface again on the next pass. + var decision googleadk.ConfirmationDecision + workflow.GetSignalChannel(ctx, googleadk.ConfirmationSignalName).Receive(ctx, &decision) + res.Approved = decision.Confirmed + + // Match the decision to the pending confirmation and resume the run with + // it as the next message. ADK re-dispatches (or blocks) the original tool + // call based on Confirmed. + if decision.FunctionCallID == "" { + decision.FunctionCallID = pending[0].FunctionCallID + } + msg = googleadk.ConfirmationResponse(decision) + } +} + +``` + + +## Continue-as-new for long conversations + +A conversation's history lives in the ADK session. To keep a Workflow's history bounded, snapshot the session with +`googleadk.ExportSession` and [continue-as-new](/develop/go/workflows/continue-as-new); rebuild it on the next run with +`googleadk.ImportSession`. `SessionSnapshot` is JSON-serializable (session-scoped state plus the full event history), so +every value in session state and every tool result must be JSON-encodable. + + +[googleadk/chat/workflow.go](https://github.com/temporalio/samples-go/blob/main/googleadk/chat/workflow.go) +```go +func ChatWorkflow(ctx workflow.Context, in ChatInput) error { + // A fresh in-memory session service, kept in a local so we can Export it later. + svc := session.InMemoryService() + + adkCtx := googleadk.NewContext(ctx) + + // Resume a prior conversation if this run was continued-as-new. + if in.Snapshot != nil { + if _, err := googleadk.ImportSession(adkCtx, svc, in.Snapshot); err != nil { + return err + } + } + + root, err := llmagent.New(llmagent.Config{ + Name: "assistant", + Description: "a friendly conversational assistant", + Model: googleadk.NewModel(ModelName), + Instruction: "You are a helpful assistant. Answer the user, using the conversation history for context.", + }) + if err != nil { + return err + } + + r, err := runner.New(runner.Config{ + AppName: AppName, + Agent: root, + SessionService: svc, + AutoCreateSession: true, + }) + if err != nil { + return err + } + + turns := 0 + // One agent turn runs at a time: serialize concurrent Updates so they can't + // interleave on the shared ADK session. + busy := false + + err = workflow.SetUpdateHandlerWithOptions( + ctx, + SendMessageUpdateName, + func(ctx workflow.Context, text string) (string, error) { + if err := workflow.Await(ctx, func() bool { return !busy }); err != nil { + return "", err + } + busy = true + defer func() { busy = false }() + + // Build the ADK context from this Update handler's own workflow.Context so + // the model Activity is scheduled on the handler's coroutine. + turnCtx := googleadk.NewContext(ctx) + var answer string + msg := genai.NewContentFromText(text, genai.RoleUser) + for ev, err := range r.Run(turnCtx, UserID, SessionID, msg, agent.RunConfig{}) { + if err != nil { + return "", err + } + if ev == nil || ev.Content == nil { + continue + } + for _, p := range ev.Content.Parts { + if p != nil && p.Text != "" { + answer = p.Text + } + } + } + turns++ + return answer, nil + }, + workflow.UpdateHandlerOptions{ + Validator: func(ctx workflow.Context, text string) error { + if text == "" { + return fmt.Errorf("message must not be empty") + } + return nil + }, + }, + ) + if err != nil { + return err + } + + // Serve messages until Temporal suggests continue-as-new (history getting large) + // or the demo turn cap is reached. + if err := workflow.Await(ctx, func() bool { + return workflow.GetInfo(ctx).GetContinueAsNewSuggested() || (in.MaxTurns > 0 && turns >= in.MaxTurns) + }); err != nil { + return err + } + + // Let any in-flight Update finish so its turn is captured in the snapshot. + if err := workflow.Await(ctx, func() bool { return workflow.AllHandlersFinished(ctx) }); err != nil { + return err + } + + snap, err := googleadk.ExportSession(adkCtx, svc, AppName, UserID, SessionID) + if err != nil { + return err + } + return workflow.NewContinueAsNewError(ctx, ChatWorkflow, ChatInput{ + Snapshot: snap, + MaxTurns: in.MaxTurns, + }) +} + +``` + + +## Streaming + +`googleadk.NewModel(name, googleadk.WithStreaming(topic, 0))` drives the model in streaming mode: the `InvokeModel` +Activity calls the model with `stream=true`, heartbeats, and publishes each chunk to a per-run +[`workflowstreams`](https://pkg.go.dev/go.temporal.io/sdk/contrib/workflowstreams) topic for external (UI) consumers, +then returns the aggregated final response into the Workflow so replay stays deterministic. + +Call `googleadk.StreamServer(ctx)` once near the top of the Workflow that drives `r.Run`, and set +`agent.RunConfig{StreamingMode: agent.StreamingModeSSE}`: + +```go +func StreamingAgentWorkflow(ctx workflow.Context, q string) (string, error) { + if err := googleadk.StreamServer(ctx); err != nil { // required when streaming + return "", err + } + topic := "run-" + workflow.GetInfo(ctx).WorkflowExecution.ID + root, _ := llmagent.New(llmagent.Config{ + Model: googleadk.NewModel("gemini-2.0-flash", googleadk.WithStreaming(topic, 0)), + // ... + }) + // ... build the runner, set agent.RunConfig{StreamingMode: agent.StreamingModeSSE}, and drive r.Run +} +``` + +External consumers read chunks with `workflowstreams.NewClient(c, workflowID, ...).Subscribe(...)`. The bidirectional +`RunLive` path (hard-coded goroutines/channels) is **not** supported. + +## Error handling + +Model, tool, and MCP failures surface as Temporal `ApplicationError`s tagged `googleadk.ModelError`, `.ToolError`, and +`.McpError`. Classify them with `googleadk.IsNonRetryable(err)` rather than string-matching. For model calls, the +upstream HTTP status drives retryability (`408`/`409`/`429`/`5xx` are retryable; other `4xx` are not). + +:::tip + +Disable your model client's own retries in the `ModelFactory`. `InvokeModel` already runs under Temporal's +`RetryPolicy`; leaving the model SDK's retries on retries a transient failure twice over. Let Temporal own retries. + +::: + +## Composing with other plugins + +The Temporal-side Activities use the default JSON data converter and ship no client/worker interceptor, so this +integration composes with Temporal interceptor- or converter-based plugins (for example +[`sdk-go/contrib/opentelemetry`](https://pkg.go.dev/go.temporal.io/sdk/contrib/opentelemetry)) without conflict. ADK +emits its own OpenTelemetry spans; register your tracing interceptor on the worker as usual. + +## Testing without a live LLM + +The plugin ships test helpers so you can unit-test agent Workflows with no network: `FakeModel` (with `TextResponse` / +`FunctionCallResponse` builders) and `FakeMCPServer`. Register them through the same `googleadk.Config` your production +worker uses. + +## Supported and not-yet-supported + +- **Supported:** single- and multi-agent (`SubAgents`) trees, in-workflow function tools, `ActivityAsTool`, stateless + MCP, Gemini built-in tools (executed server-side inside `InvokeModel`), human-in-the-loop tool confirmation, + continue-as-new session-state carry, the in-memory session service, and SSE streaming. +- **Not yet:** `RunLive` (bidirectional streaming), sub-agent-as-child-workflow, live memory/artifact tools that require + in-workflow network I/O, and database/Vertex session services. These raise or are documented rather than silently + degrading. + +## Samples + +The [Google ADK plugin samples](https://github.com/temporalio/samples-go/tree/main/googleadk) demonstrate a basic agent +with a tool, a [multi-agent](https://github.com/temporalio/samples-go/tree/main/googleadk/multiagent) system, durable +[human-in-the-loop](https://github.com/temporalio/samples-go/tree/main/googleadk/humanintheloop) tool approval, and a +[continue-as-new chat](https://github.com/temporalio/samples-go/tree/main/googleadk/chat). diff --git a/docs/develop/go/integrations/index.mdx b/docs/develop/go/integrations/index.mdx new file mode 100644 index 0000000000..a8b37e1574 --- /dev/null +++ b/docs/develop/go/integrations/index.mdx @@ -0,0 +1,22 @@ +--- +id: index +title: Integrations - Go SDK +sidebar_label: Integrations +description: This section covers integrations with the Go SDK +toc_max_heading_level: 4 +keywords: + - Go SDK + - integrations + - ai frameworks +tags: + - Go SDK + - Temporal SDKs + - Integrations +--- + +import IntegrationsGrid from '@site/src/components/IntegrationsGrid'; + +The following integrations are available for the Temporal Go SDK. +You can also use the Temporal Go SDK's [Plugin system](/develop/plugins-guide) to build your own integrations. + + diff --git a/sidebars.js b/sidebars.js index 4dab1212bf..d7c89ec3a6 100644 --- a/sidebars.js +++ b/sidebars.js @@ -232,6 +232,17 @@ module.exports = { }, ], }, + { + type: 'category', + label: 'Integrations', + className: 'sidebar-integrations', + collapsed: true, + link: { + type: 'doc', + id: 'develop/go/integrations/index', + }, + items: ['develop/go/integrations/google-adk'], + }, ], }, { diff --git a/src/components/IntegrationsGrid/index.tsx b/src/components/IntegrationsGrid/index.tsx index 707af4bd92..b19649e926 100644 --- a/src/components/IntegrationsGrid/index.tsx +++ b/src/components/IntegrationsGrid/index.tsx @@ -10,12 +10,13 @@ import integrations, { type SDK, type Integration } from "./integrations-data"; import SdkSvg from "../elements/SdkSvgs/SdkSvg"; import styles from "./IntegrationsGrid.module.css"; -const ALL_SDKS: SDK[] = ["Java", "Python", "Ruby", "TypeScript"]; +const ALL_SDKS: SDK[] = ["Go", "Java", "Python", "Ruby", "TypeScript"]; const LANGUAGE_AGNOSTIC = "Language-agnostic"; type SdkFilter = SDK | typeof LANGUAGE_AGNOSTIC; const ALL_SDK_FILTERS: SdkFilter[] = [...ALL_SDKS, LANGUAGE_AGNOSTIC]; const SDK_BLOCK_NAMES: Record = { + Go: "goLangBlock", Java: "javaBlock", Python: "pythonBlock", Ruby: "rubyBlock", diff --git a/src/components/IntegrationsGrid/integrations-data.json b/src/components/IntegrationsGrid/integrations-data.json index 6abe200f5e..b4d0c791e8 100644 --- a/src/components/IntegrationsGrid/integrations-data.json +++ b/src/components/IntegrationsGrid/integrations-data.json @@ -62,6 +62,15 @@ "sdk": "Python", "href": "https://adk.dev/integrations/temporal/" }, + { + "name": "Google ADK", + "description": "Run Google ADK agents with durable execution using the Temporal Go SDK.", + "tags": [ + "Agent framework" + ], + "sdk": "Go", + "href": "/develop/go/integrations/google-adk" + }, { "name": "Grafana Cloud", "description": "Export Temporal Cloud metrics to Grafana Cloud with a serverless integration and pre-built dashboard.", diff --git a/src/components/IntegrationsGrid/integrations-data.ts b/src/components/IntegrationsGrid/integrations-data.ts index 537a282c89..3b3729c443 100644 --- a/src/components/IntegrationsGrid/integrations-data.ts +++ b/src/components/IntegrationsGrid/integrations-data.ts @@ -1,6 +1,6 @@ import integrationsData from "./integrations-data.json"; -export type SDK = "Java" | "Python" | "TypeScript" | "Ruby"; +export type SDK = "Go" | "Java" | "Python" | "TypeScript" | "Ruby"; export type Integration = { name: string;