feat(llm): add config-driven LLM provider package [AG-000] - #674
Conversation
Add pkg/llm, a vendor-agnostic LLM provider package built on langchaingo: the Provider interface + chat/message contract types, per-vendor adapters (anthropic, openai, litellm, ollama, vertex, bedrock), and a config-driven registry (Resolve/New/NewFromConfig) that reads provider selection, API keys, base URLs, and gateway headers from a configuration.Configuration. Settings resolve from env vars via SetSupportedEnvVars + AddAlternativeKeys (ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_CLOUD_*, AWS_REGION, LITELLM_*, SNYK_LLM_EXTRA_HEADERS with REMY_EXTRA_HEADERS kept as an alternative key); API keys are read on demand and never persisted to storage. An optional NetworkAccess threads the unauthorized HTTP transport (proxy/CA/ FIPS, no Snyk auth headers) under the provider clients; WithBedrockAppID adds the AWS User-Agent app id so the package ships vendor-neutral. Ports the provider test suite, adds a config-driven registry test and a networking-compose test, generates pkg/mocks/llm.go, and assigns /pkg/llm/ to @snyk/agentic-code-security in CODEOWNERS. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
⛔ Snyk checks have failed. 5 issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
⛔ Snyk checks have failed. 5 issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
PR Reviewer Guide 🔍
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 4 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ebf453a. Configure here.
| func (m *vertexModel) generateClaude(ctx context.Context, model string, messages []llms.MessageContent, co *llms.CallOptions) (*llms.ContentResponse, error) { | ||
| if m.claude == nil { | ||
| return nil, fmt.Errorf("vertex: anthropic client not initialised (provider was configured for a Gemini model)") | ||
| } |
There was a problem hiding this comment.
Vertex cross-family model override
High Severity
vertexModel builds either a Gemini (genai) or Claude (anthropic-sdk-go) client from the default model at construction, but GenerateContent picks the backend from each call’s WithModel override. A per-request model in the other family hits generateGemini or generateClaude with a nil client and fails, even though the type documents per-call model overrides.
Reviewed by Cursor Bugbot for commit ebf453a. Configure here.
| ToolCallID: tr.ToolCallID, | ||
| Content: tr.Content, | ||
| }) | ||
| } |
There was a problem hiding this comment.
Tool error flag ignored
Medium Severity
ToolResult exposes IsError, but mapping into provider messages never uses it: toLCMessage only copies ToolCallID and Content, and the Vertex Claude path always passes false into NewToolResultBlock. Failed tool runs are sent as successful results to the model.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit ebf453a. Configure here.
| // (tool_result blocks in a single user message), like the native anthropic | ||
| // adapter — so we do NOT split tool results the way the OpenAI-family | ||
| // adapters require. | ||
| return NewLangchainAdapter("bedrock", llm), nil |
There was a problem hiding this comment.
Bedrock skips temperature strip
Medium Severity
Anthropic and OpenAI adapters strip langchaingo’s hardcoded temperature field via headerTransport, but NewBedrockAdapter wraps langchaingo Bedrock with no equivalent HTTP middleware. Claude-on-Bedrock requests can still send deprecated temperature:0 and be rejected by the model.
Reviewed by Cursor Bugbot for commit ebf453a. Configure here.
| // so it is not a hard requirement here. | ||
| build: func(config configuration.Configuration, res Resolution, o *options) (Provider, error) { | ||
| return NewBedrockAdapter(res.Model, config.GetString(CONFIG_AWS_REGION), o.bedrockAppID) | ||
| }, |
There was a problem hiding this comment.
Bedrock ignores network transport
Medium Severity
WithNetworkAccess threads the unauthorized proxy/CA transport into other providers, but the Bedrock registry build function and NewBedrockAdapter never accept or apply that RoundTripper. Bedrock traffic uses the AWS SDK default HTTP client and bypasses corporate proxy/TLS settings used elsewhere.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit ebf453a. Configure here.
Add godoc Example functions covering the common entry points: NewFromConfig (one-shot), option wiring (network access + Bedrock app id), Resolve+New for telemetry, and the tool-calling round-trip. Compile-checked by go test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>


What
Adds
pkg/llm, a vendor-agnostic LLM provider package (built on langchaingo), so LLM access can be shared across Snyk tools instead of living in theremyextension.Providerinterface +ChatRequest/ChatResponse/Message/ToolCall/… types.config.go+registry.go):Resolve/New/NewFromConfigread provider selection, API keys, base URLs, and gateway headers from aconfiguration.Configuration.snyk_llm_*keys wired viaSetSupportedEnvVars+AddAlternativeKeys. Existing env vars keep working (ANTHROPIC_API_KEY,OPENAI_API_KEY,GOOGLE_CLOUD_*,AWS_REGION,LITELLM_*).SNYK_LLM_EXTRA_HEADERSis the new primary name;REMY_EXTRA_HEADERSretained as an alternative key. API keys are read on demand, never persisted to storage.WithNetworkAccessthreads the unauthorized HTTP transport (proxy/CA/FIPS, no Snyk auth headers) under provider clients.WithBedrockAppIDsets the AWS User-Agent app id; the package ships unbranded (theSNYK_AGENTIC_FIXid is supplied by the caller).pkg/mocks/llm.go;/pkg/llm/assigned to@snyk/agentic-code-securityin CODEOWNERS.Ports the full provider test suite and adds a config-driven registry test + a networking-compose test.
go build/vet/test ./pkg/llm/...clean;go mod tidystable.This pulls
langchaingo,aws-sdk-go-v2/{config,bedrockruntime},google.golang.org/genai,anthropics/anthropic-sdk-go, andgolang.org/x/oauth2/googleinto GAF's graph — inherited by snyk/cli and every GAF consumer. Please confirm this is acceptable, or whetherpkg/llmshould be isolated (separatego.mod/ build tags) before merge.Consumer
The
remyextension is migrating off itsinternal/providerto this package (companion PR in snyk/remy-cli-extension). CODEOWNERS routes/pkg/llm/review to Agentic Code Security.🤖 Generated with Claude Code
Note
High Risk
Large new surface area with third-party LLM credentials, gateway auth stripping, and heavy dependencies inherited by all GAF consumers; misconfiguration could leak keys or send code to the wrong endpoint.
Overview
Introduces
pkg/llm, a shared LLM layer for GAF: aProvidercontract (ChatCompletion, tools, normalized stop reasons) plusResolve/New/NewFromConfigthat readconfiguration.Configurationand familiar env vars (ANTHROPIC_*,OPENAI_*,GOOGLE_CLOUD_*,LITELLM_*,AWS_REGION, optionalSNYK_LLM_EXTRA_HEADERSwith legacyREMY_EXTRA_HEADERS).Backends are wired through langchaingo and small custom
llms.Modelshims: Anthropic, OpenAI, LiteLLM (OpenAI-compatible proxy), Ollama (/api/chatwith tools), Vertex (Gemini viagenai, Claude on Model Garden viaanthropic-sdk-go), and Bedrock. Shared behavior includes gateway header injection, placeholder keys stripped on the wire when unset, OpenAI/LiteLLM HTTPS base-URL normalization, temperature stripped from JSON bodies, per-call timeouts, and 429/529 backoff. OptionalWithNetworkAccessuses the unauthorized HTTP transport (proxy/CA without Snyk auth);WithBedrockAppIDbrands Bedrock traffic.Also adds
FakeProvider, generatedpkg/mocks/llm.go, CODEOWNERS for Agentic Code Security, and new direct deps (langchaingo, AWS Bedrock SDK,google.golang.org/genai,anthropic-sdk-go, etc.) on the root module.Reviewed by Cursor Bugbot for commit ebf453a. Bugbot is set up for automated code reviews on this repo. Configure here.
Usage
Runnable godoc examples are in
pkg/llm/example_test.go. In short:Inside a GAF workflow — thread the shared transport and (for Bedrock) the app id:
Resolve then build (when you need the provider name/model for telemetry first):
Tool calling — pass
Tools, then echo results back asToolResult:Provider selection — explicit via
config.Set(llm.CONFIG_PROVIDER, "anthropic")(or--provider); otherwise auto-detected fromANTHROPIC_API_KEY(orANTHROPIC_BASE_URL) thenOPENAI_API_KEY. Other providers readGOOGLE_CLOUD_PROJECT/GOOGLE_CLOUD_LOCATION,AWS_REGION,LITELLM_BASE_URL/LITELLM_API_KEY, and gateway headers fromSNYK_LLM_EXTRA_HEADERS.