Tags: google/adk-go
Tags
fix(remoteagent): reject card sources that are neither http(s) URLs n… …or paths (#1328) (#1486) * fix(remoteagent): reject card sources that are neither http(s) URLs nor paths NewAgentCardProvider picked its resolution path with strings.HasPrefix on "http://" or "https://", and every other string fell through to os.ReadFile. So a source that names a scheme the provider cannot serve was read off disk as a path instead of being rejected. "file:///opt/card.json" reached os.ReadFile verbatim and failed with "no such file or directory", naming a path the caller never wrote. A typo such as "htp://example.com" failed the same way. The prefix test was also case sensitive, so "HTTP://example.com" was sent to os.ReadFile rather than fetched. Classify the source on its scheme instead: http and https are fetched, no scheme is a path, and anything else is an error that says so. The classification is unexported and sits next to its only caller, so this adds nothing to any API. The scheme comes from a url.Parse of the part up to the first colon, not of the whole source. A perfectly ordinary path is often not a valid URL, so parsing the whole source leaves a parse failure to interpret, and neither reading is right: "reject" turns away "/tmp/100%.json" for its escape, while "treat it as a path" waves an unsupported scheme through on the one trailing character that spoils the parse, which is the confusion this is meant to end. Parsing only the prefix leaves net/url to say what a scheme may contain, and no source is unparseable that far. A Windows drive letter is a one-character scheme and stays a path. This is not a security fix. The source is a constructor parameter, and nothing in the repository lets an HTTP route, an A2A peer, an LLM tool or the YAML config loader supply it, so a caller who can set it can already write whatever Go they like. agentregistry passes a whole AgentCard and does not reach this code at all. Behaviour change: a source whose scheme is neither http nor https is now an error rather than a file read. Those reads failed anyway, with a worse message. An uppercase scheme such as HTTP:// now fetches. * fix(remoteagent): classify a card source on scheme://, export the sentinel (cherry picked from commit 681ac5d) Co-authored-by: Sasha <39381081+jjsasha63@users.noreply.github.com>
fix(compaction): carry durable facts across re-summarization (#1431) * fix(compaction): carry durable facts across re-summarization Tail retention seeds each summary with the previous one, so a value stated early is recompressed on every pass. The default summarizer prompt asked for a "concise" summary and to reiterate the user request, which under a rolling summary is an instruction to describe the most recent turns. Measured against a real model with eight arbitrary facts stated early and probed once the events holding them had been summarized away, that prompt lost every one of them in five conversations out of ten, and lost none in the other five. The loss is not gradual: each pass either copies a value forward or generalizes it to its label -- "the deployment region" for "europe-west4" -- and once generalized it cannot come back, because the next pass sees only the previous summary and the retained tail. The agent then reports that it does not have the information rather than inventing it, so the failure is quiet. An intermittent default is worse than a consistent one here: it passes whatever spot check someone runs before trusting it, then discards the early conversation in production. The prompt now requires concrete values to be listed and copied forward verbatim, and forbids generalizing them to save space. Under the same measurement that lost nothing across nine conversations. It costs about twice as many summarizer calls, because larger summaries cross TokenThreshold sooner, which is the trade this feature cannot avoid: bounding the prompt and retaining everything are not both available. The package doc claimed tail retention as the strategy that bounds prompt growth without saying what bounding costs; it now does, and points at session state for detail that must not be lost at all. * docs(examples): add a harness that measures compaction recall The compaction settings are measured by prompt size, and prompt size does not say whether the conversation survived. This plants checkable facts, buries them under filler until compaction has summarized them away, then asks about each one, so a config can be judged on whether the agent can still answer. It runs each arm several times by default and prints the per-run scores rather than only an average. Recall here is close to all-or-nothing -- a pass either copies a value forward or generalizes it to its label, and a label cannot be turned back into a value -- so configs tend to score full marks or nothing, and an average over too few runs describes neither. The terse arm supplies the summarizer prompt as it was before durable facts were carried forward. It stands in for any custom PromptTemplate that asks only for a concise summary, and it makes the cost of that choice reproducible: on one model, one config and the same conversation, the default recalled 8/8 and the terse prompt 0/8 over the same 20 compaction passes. * fix(examples): compare against the real prior prompt, not a shortened one Review found the harness could not reproduce the result it shipped to demonstrate. Its comparison arm was a hand-shortened prompt: it dropped the CRITICAL INSTRUCTIONS framing, the language instruction and the tool-name instruction, and reworded the closing sentence. That made the run a comparison against a third prompt rather than against the previous default, so it did not isolate the instruction this change adds. The arm is now the pre-change default reproduced verbatim, and renamed to prior since that is what it is. Re-measured, the conclusion is stronger than the one the wrong prompt produced: 24/24 for the default against 0/8 twice for the prior prompt, on the same model and config. Also from review: - buriedFacts matched fact labels with a substring test, so the label "7" matched INC-77312 and tarn-staging-91 and marked that fact buried on unrelated events. It now uses the same word-boundary matchers as scoring. - The disclaimer list had grown broad enough to catch correct answers: "I am unable to browse, but the region is europe-west4" scored as a miss. Narrowed to unambiguous denials, since the NO_RECORD sentinel already carries that job and a false miss flatters the change under test. - truncate sliced bytes and could split a multi-byte rune. A failed run no longer aborts the batch. A run is hundreds of model calls and a busy model returns 503 regularly; losing the runs that already succeeded to one transient error made the harness impractical at the sizes it asks for. The prompt comment now says why instruction 3 covers only user-stated values, rather than leaving a reader to guess whether tool-derived ones were considered, and names the one behavioural consequence: content arriving in a tool response now has a prompt arguing for its retention where it used to decay out.
fix(skilltoolset): accept scalar allowed-tools frontmatter (#1301) (#… …1310) * fix(skilltoolset): accept scalar allowed-tools frontmatter * test(skilltoolset): cover allowed-tools source listing * fix(skilltoolset): reject unbalanced allowed-tools patterns (cherry picked from commit 9584b47) Co-authored-by: ktsoator <ktsoator@gmail.com>
chore(deps): bump the go-minor-patch group with 6 updates (#1275) Bumps the go-minor-patch group with 6 updates: | Package | From | To | | --- | --- | --- | | [github.com/modelcontextprotocol/go-sdk](https://github.com/modelcontextprotocol/go-sdk) | `1.6.1` | `1.7.0` | | [github.com/openai/openai-go/v3](https://github.com/openai/openai-go) | `3.46.0` | `3.49.0` | | [google.golang.org/api](https://github.com/googleapis/google-api-go-client) | `0.287.1` | `0.291.0` | | [google.golang.org/genai](https://github.com/googleapis/go-genai) | `1.65.0` | `1.66.0` | | [google.golang.org/grpc](https://github.com/grpc/grpc-go) | `1.82.1` | `1.83.0` | | [github.com/a2aproject/a2a-go/v2](https://github.com/a2aproject/a2a-go) | `2.3.1` | `2.4.0` | Updates `github.com/modelcontextprotocol/go-sdk` from 1.6.1 to 1.7.0 - [Release notes](https://github.com/modelcontextprotocol/go-sdk/releases) - [Commits](modelcontextprotocol/go-sdk@v1.6.1...v1.7.0) Updates `github.com/openai/openai-go/v3` from 3.46.0 to 3.49.0 - [Release notes](https://github.com/openai/openai-go/releases) - [Changelog](https://github.com/openai/openai-go/blob/main/CHANGELOG.md) - [Commits](openai/openai-go@v3.46.0...v3.49.0) Updates `google.golang.org/api` from 0.287.1 to 0.291.0 - [Release notes](https://github.com/googleapis/google-api-go-client/releases) - [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md) - [Commits](googleapis/google-api-go-client@v0.287.1...v0.291.0) Updates `google.golang.org/genai` from 1.65.0 to 1.66.0 - [Release notes](https://github.com/googleapis/go-genai/releases) - [Changelog](https://github.com/googleapis/go-genai/blob/main/CHANGELOG.md) - [Commits](googleapis/go-genai@v1.65.0...v1.66.0) Updates `google.golang.org/grpc` from 1.82.1 to 1.83.0 - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](grpc/grpc-go@v1.82.1...v1.83.0) Updates `github.com/a2aproject/a2a-go/v2` from 2.3.1 to 2.4.0 - [Release notes](https://github.com/a2aproject/a2a-go/releases) - [Changelog](https://github.com/a2aproject/a2a-go/blob/main/CHANGELOG.md) - [Commits](a2aproject/a2a-go@v2.3.1...v2.4.0) --- updated-dependencies: - dependency-name: github.com/modelcontextprotocol/go-sdk dependency-version: 1.7.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-minor-patch - dependency-name: github.com/openai/openai-go/v3 dependency-version: 3.49.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-minor-patch - dependency-name: google.golang.org/api dependency-version: 0.291.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-minor-patch - dependency-name: google.golang.org/genai dependency-version: 1.66.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-minor-patch - dependency-name: google.golang.org/grpc dependency-version: 1.83.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-minor-patch - dependency-name: github.com/a2aproject/a2a-go/v2 dependency-version: 2.4.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-minor-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
OpenAI support (#1178) * Add support for the OpenAPI models This PR enables basic support for OpenAI models (and endpoints that expose a OpenAI API compatible API interface). I am stressing on *basic* support because we will leave tool calling to the next PR :) * Run go mod tidy * refactor: rename package to openaimodel, introduce ClientConfig, and propagate response metadata in streams * refactor: add support for logprobs conversion and update finish reason handling for OpenAI responses * refactor: replace raw error strings with sentinel errors and add comprehensive unit test coverage for OpenAI model converters * docs(examples/openai): add OpenAI model integration sample Adds a runnable example showing an ADK llmagent backed by the OpenAI model from #1178 (openaimodel.NewModel) instead of Gemini. The only difference from the Gemini quickstart is the model constructor; agents, the functiontool, the runner, and the launcher are unchanged. The sample registers a get_weather function tool to exercise OpenAI tool calling, defaults to gpt-4o-mini, and supports OpenAI-compatible endpoints via OPENAI_BASE_URL. A README follows the examples/workflow sample layout. * refactor: move openai model package to openaimodel and reorganize internal structure * feat: enable strict mode for OpenAI structured outputs by enforcing schema requirements * docs: mark openaimodel package as experimental * fix: track function names in stream translator to handle missing names in done events and cleanup unused schema test code. --------- Co-authored-by: Levi Gross <levi@levigross.com> Co-authored-by: Levi Gross <levigross@users.noreply.github.com> Co-authored-by: wolo <wolo@google.com>
fix(parallelagent): await sub-agent teardown before returning on earl… …y stop (#1186) (#1188) * fix(parallelagent): await sub-agent teardown before returning on early stop On early consumer stop, the returned iterator closed doneChan and returned immediately without joining the sub-agent goroutines. A sub-agent's deferred teardown (e.g. remoteagent's CancelTask RPC) could therefore outlive the run and touch a request-scoped context whose lifetime had already ended; in google3 this panics on a detached census context and cascades into DEADLINE_EXCEEDED in prod. Drain resultsChan in the iterator's deferred cleanup. The funnel goroutine closes resultsChan only after errGroup.Wait(), so draining it blocks until every sub-agent goroutine (and its deferred teardown) has returned. This is deadlock-free: on the normal path the outer loop already exited because the funnel closed resultsChan (drain returns at once); on early stop, close(doneChan) unblocks the sub-agents, they run teardown and return, the funnel closes resultsChan, and the drain returns. parallelagent is the only goroutine-spawning primitive in agent/ and runner/, so joining here closes the race. ## Testing Plan - go build -mod=readonly ./... - go test -race -mod=readonly -count=1 ./agent/workflowagents/parallelagent/... - golangci-lint run ./agent/workflowagents/parallelagent/... - go mod tidy -diff (prints nothing) - New regression test TestParallelAgent_AwaitsSubAgentTeardownOnEarlyStop: breaks after the first event and asserts all 3 sub-agents' teardown ran before Run returned; fails (0/3) without the fix, passes with it. * fix(parallelagent): cancel sub-agents on early stop; deterministic teardown test Address review on #1186: - On early consumer stop, cancel the sub-agent context (not just close doneChan) so a sub-agent blocked mid-run aborts promptly instead of the parent blocking until it next yields. - Tighten the iterator comment and drop the remoteagent-specific example so parallelagent's docs don't couple to a consumer; label the drain loop. - Replace the time.Sleep-based regression test with a deterministic channel barrier (gated teardown), proving Run blocks until teardown completes without wall-clock timing. Verified it still fails against the pre-fix agent.go. (cherry picked from commit 7cf1fec)
test(session/vertexai): add table-driven tests for FunctionCall/Respo… …nse mapping (#739) Add TestAiplatformToGenaiContent_FunctionCallMapping, a table-driven test that verifies aiplatformToGenaiContent correctly preserves: - ID, Name, and Args for FunctionCall parts - ID, Name, and Response for FunctionResponse parts - empty-string IDs are passed through unchanged The table-driven format matches the style used elsewhere in this file and makes it easy to add further cases.
PreviousNext