mirror of
https://github.com/akitaonrails/ai-memory.git
synced 2026-10-02 03:24:46 +08:00
fixup(opencode): fmt, changelog, config test, docs
Audit follow-ups pushed onto the PR branch (maintainer edit): - merge current main into the branch (was 56 commits behind, no conflicts) - run cargo fmt on opencode.rs / lib.rs (the rustfmt CI job was failing) - CHANGELOG [Unreleased] entries for the new provider and the compose env_file switch - config test pinning opencode / opencode-zen / opencode_zen parsing, the claude-sonnet-4-6 default model, and OPENCODE_API_KEY sourcing, following the per-provider test convention - README provider row + docs/install.md provider-defaults table row - compose comment now lists every provider option including opencode Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
378d529eca
commit
3a89394a76
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- OpenCode Zen/Go is now a first-class LLM provider:
|
||||
`AI_MEMORY_LLM_PROVIDER=opencode` (alias `opencode-zen`) routes
|
||||
consolidation through `https://opencode.ai/zen/go/v1` using the OpenAI
|
||||
chat-completions wire format. Authenticate with `OPENCODE_API_KEY`
|
||||
(key from `opencode.ai/auth`); the default model is `claude-sonnet-4-6`.
|
||||
`ai-memory llm-test --provider opencode` exercises it end-to-end ([#147]).
|
||||
- `docker/docker-compose.yml` now loads provider credentials from a
|
||||
gitignored `docker/.env` via `env_file` (optional — existing deployments
|
||||
without that file keep working), replacing the commented-out inline
|
||||
provider blocks ([#147]).
|
||||
|
||||
### Fixed
|
||||
- The detached drainer's `logs/hook-drain.log` now rotates once it exceeds
|
||||
1 MiB (previous contents move to `hook-drain.log.old`), so an agent
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
| Antigravity CLI | Supported | MCP config (`serverUrl`) + lifecycle hooks (`agy` alias). |
|
||||
| Grok Build CLI | Hooks | Lifecycle hooks via `install-hooks --agent grok` (`~/.grok/hooks/ai-memory.json`, Grok-specific hook bundle, native `--agent grok`). Capture works; no handoff injection — Grok ignores `SessionStart` stdout, so recover handoffs via MCP `memory_handoff_accept`. |
|
||||
| VS Code Copilot | MCP-only | `.vscode/mcp.json` for Copilot agent mode; no lifecycle hooks (Copilot does not expose them yet). |
|
||||
| LLM/auth providers | Supported | Anthropic, OpenAI, OpenAI OAuth/Codex, GitHub Copilot, Gemini, OpenAI-compatible endpoints, and generic OIDC device auth for native hooks. |
|
||||
| LLM/auth providers | Supported | Anthropic, OpenAI, OpenAI OAuth/Codex, GitHub Copilot, Gemini, OpenCode Zen/Go, OpenAI-compatible endpoints, and generic OIDC device auth for native hooks. |
|
||||
| Embedding providers | Supported | OpenAI, Voyage, and Google Gemini. |
|
||||
|
||||
## What it is
|
||||
|
||||
@@ -1335,6 +1335,36 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn opencode_provider_resolves_choice_default_model_and_api_key() {
|
||||
for spelling in ["opencode", "opencode-zen", "opencode_zen"] {
|
||||
let cfg = Config {
|
||||
llm_provider: Some(spelling.into()),
|
||||
runtime_env: RuntimeEnv {
|
||||
opencode_api_key: Some(SecretString::from("sk-opencode-test")),
|
||||
..RuntimeEnv::default()
|
||||
},
|
||||
..Config::default()
|
||||
};
|
||||
|
||||
let provider = cfg.llm_provider_config().unwrap().unwrap();
|
||||
assert_eq!(provider.provider, ProviderChoice::OpenCode, "{spelling}");
|
||||
assert_eq!(provider.model, "claude-sonnet-4-6", "{spelling}");
|
||||
assert_eq!(
|
||||
provider.auth.requirement(),
|
||||
AuthRequirement::RequiredApiKey {
|
||||
env_var: "OPENCODE_API_KEY"
|
||||
},
|
||||
"{spelling}"
|
||||
);
|
||||
assert_eq!(
|
||||
provider.auth.require_api_key().unwrap().expose_secret(),
|
||||
"sk-opencode-test",
|
||||
"{spelling}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn anthropic_oauth_provider_underscore_alias_also_resolves() {
|
||||
let cfg = Config {
|
||||
|
||||
@@ -71,10 +71,10 @@ pub use oidc::{
|
||||
};
|
||||
pub use openai::OpenAiProvider;
|
||||
pub use openai_compat::OpenAiCompatProvider;
|
||||
pub use opencode::{OPENCODE_DEFAULT_MODEL, OPENCODE_ZEN_BASE_URL, OpenCodeProvider};
|
||||
pub use openai_oauth::{
|
||||
CODEX_CLIENT_ID, CODEX_RESPONSES_URL, OPENAI_OAUTH_AUTH_URL, OPENAI_OAUTH_ISSUER,
|
||||
OPENAI_OAUTH_TOKEN_URL, OpenAiOAuthProvider, OpenAiOAuthToken, OpenAiOAuthTokenResponse,
|
||||
};
|
||||
pub use opencode::{OPENCODE_DEFAULT_MODEL, OPENCODE_ZEN_BASE_URL, OpenCodeProvider};
|
||||
pub use provider::{LlmProvider, complete_structured};
|
||||
pub use types::{ChatMessage, ChatRequest, ChatResponse, Role, Usage};
|
||||
|
||||
@@ -33,8 +33,7 @@ impl OpenCodeProvider {
|
||||
/// # Errors
|
||||
/// Returns a `reqwest::Error` if the HTTP client cannot be built.
|
||||
pub fn new(api_key: SecretString, model: impl Into<String>) -> LlmResult<Self> {
|
||||
let inner =
|
||||
OpenAiCompatProvider::new(OPENCODE_ZEN_BASE_URL, Some(api_key), model.into())?;
|
||||
let inner = OpenAiCompatProvider::new(OPENCODE_ZEN_BASE_URL, Some(api_key), model.into())?;
|
||||
Ok(Self { inner })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,9 +28,10 @@ services:
|
||||
required: false
|
||||
environment:
|
||||
- RUST_LOG=ai_memory=info,ai_memory_store=info,ai_memory_wiki=info,ai_memory_mcp=info,tracing_appender=warn
|
||||
# LLM provider configured via docker/.env (gitignored).
|
||||
# Options: anthropic | openai | openai-compat | gemini | copilot
|
||||
# See .env.production.example for full reference.
|
||||
# LLM provider configured via docker/.env (gitignored). Options:
|
||||
# anthropic | openai | openai-oauth | anthropic-oauth | copilot |
|
||||
# gemini | openai-compat | opencode
|
||||
# See .env.production.example for the full reference.
|
||||
healthcheck:
|
||||
test: ["CMD", "/usr/local/bin/ai-memory", "status"]
|
||||
interval: 30s
|
||||
|
||||
@@ -806,6 +806,7 @@ If you set only the provider, ai-memory picks a sensible default:
|
||||
| `AI_MEMORY_LLM_PROVIDER=openai-oauth` | `gpt-5.5` | ChatGPT/Codex backend. Run `ai-memory auth login openai-oauth` once; ai-memory stores the refresh token in `<data_dir>/auth.json` and refreshes access tokens automatically. |
|
||||
| `AI_MEMORY_LLM_PROVIDER=copilot` | `gpt-5.5` | GitHub Copilot Chat backend. ai-memory stores a GitHub user token in `<data_dir>/auth.json`, exchanges it for a short-lived Copilot API token, and refreshes before expiry. |
|
||||
| `AI_MEMORY_LLM_PROVIDER=gemini` | `gemini-2.5-flash` | Google's hosted option with a generous free tier. ai-memory disables Gemini 2.5 Flash's default dynamic thinking so hidden thought tokens do not truncate strict JSON. Set `GEMINI_API_KEY` (or `GOOGLE_API_KEY`). |
|
||||
| `AI_MEMORY_LLM_PROVIDER=opencode` | `claude-sonnet-4-6` | [OpenCode Zen/Go](https://opencode.ai) cloud API — OpenAI-compatible endpoint at `opencode.ai/zen/go/v1`. Set `OPENCODE_API_KEY` (key from `opencode.ai/auth`). Alias: `opencode-zen`. |
|
||||
| `AI_MEMORY_EMBEDDING_PROVIDER=openai` | `text-embedding-3-small` (1536-dim) | 5× cheaper than `-3-large` with marginal recall loss. |
|
||||
| `AI_MEMORY_EMBEDDING_PROVIDER=openai` + `AI_MEMORY_EMBEDDING_BASE_URL=https://openrouter.ai/api/v1` | `openai/text-embedding-3-small` via [OpenRouter](https://openrouter.ai) | Reuses `LLM_API_KEY` or `OPENAI_API_KEY` with the OpenAI-compatible embedding client. |
|
||||
| `AI_MEMORY_EMBEDDING_PROVIDER=voyage` | `voyage-3` (1024-dim) | Voyage's current general-purpose recommendation. |
|
||||
|
||||
Reference in New Issue
Block a user