Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Before calling any change done, `cargo fmt --all`, the `clippy` line above, and

## Workspace

37 crates organized into six layers, with `goat-protocol` at the bottom of the dependency DAG:
56 crates organized into six layers, with `goat-protocol` at the bottom of the dependency DAG:

**Infrastructure**
- `goat-protocol` — shared wire contract (`Op`, `Event`, `TaskId`); serde only; leaf.
Expand All @@ -37,9 +37,19 @@ Before calling any change done, `cargo fmt --all`, the `clippy` line above, and
- `goat-provider` — the `Provider` trait; leaf. Key types: `Provider`, `Request` (incl. `ToolChoice`), `StreamEvent`, `StreamError`, `Message`, `Capabilities`, `Model`, `ProviderId`, `ContentBlock`. Providers classify their own wire errors into `StreamError` structurally (`error.rs` per provider); the engine never inspects error strings.
- `goat-provider-anthropic` — Anthropic Claude API provider; per-model context windows, prompt-caching `cache_control` breakpoints (tools + system + last two messages), `stop_reason` overflow detection.
- `goat-provider-gemini` — Google Gemini provider; API key (Generative Language API) or OAuth (Code Assist free tier, gemini-cli compatible); four modules: `lib` (provider orchestration), `wire` (Gemini request/response format), `oauth` (Google OAuth PKCE flow), `codeassist` (Code Assist envelope + project onboarding).
- `goat-provider-openai-compat` — OpenAI-family HTTP clients; three modules: `chat` (Chat Completions API, used by local providers), `responses` (Responses API, used by OpenAI and Codex), `common` (shared client/validate/discovery helpers).
- `goat-provider-openai-compat` — OpenAI-family HTTP clients; modules: `chat` (Chat Completions API), `responses` (Responses API), `hosted` (API-key builder + HTTPS host pinning), `common`, `vision`.
- `goat-provider-openai` — OpenAI provider (wraps `responses` module).
- `goat-provider-openai-codex` — OpenAI Codex provider (wraps `responses` module).
- `goat-provider-openrouter` — OpenRouter API-key provider; Chat Completions via `hosted::api_key`.
- `goat-provider-groq` — Groq API-key provider.
- `goat-provider-deepseek` — DeepSeek API-key provider.
- `goat-provider-mistral` — Mistral API-key provider.
- `goat-provider-zai` — Z.AI API-key provider; catalog-only validation/discovery.
- `goat-provider-zai-coding` — Z.AI Coding Plan API-key provider (distinct credential from `zai`).
- `goat-provider-kimi` — Moonshot Kimi API-key provider.
- `goat-provider-kimi-code` — Kimi Code OAuth device-code provider; owns `oauth` module and `KimiCodeProvider`.
- `goat-provider-qwen` — Qwen DashScope API-key provider; optional `--endpoint` for non-US workspaces.
- `goat-provider-xai` — xAI Grok provider; API key (Chat Completions) or SuperGrok/X Premium+ OAuth (Responses API); owns `oauth` module.
- `goat-provider-local` — table-driven local-inference provider (Ollama, LM Studio, llama.cpp); wraps `chat` module.
- `goat-providers` — provider registry; wires all provider crates. `Registry::new(store)` for default account, `Registry::load(store, account)` for explicit. `Registry::login(provider, status)` dispatches OAuth login through the `Provider::login` trait method.

Expand Down
102 changes: 99 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 10 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,16 @@ goat-provider = { path = "crates/goat-provider" }
goat-auth = { path = "crates/goat-auth" }
goat-store = { path = "crates/goat-store" }
goat-provider-openai-compat = { path = "crates/goat-provider-openai-compat" }
goat-provider-hosted = { path = "crates/goat-provider-hosted" }
goat-provider-openrouter = { path = "crates/goat-provider-openrouter" }
goat-provider-groq = { path = "crates/goat-provider-groq" }
goat-provider-deepseek = { path = "crates/goat-provider-deepseek" }
goat-provider-mistral = { path = "crates/goat-provider-mistral" }
goat-provider-zai = { path = "crates/goat-provider-zai" }
goat-provider-zai-coding = { path = "crates/goat-provider-zai-coding" }
goat-provider-kimi = { path = "crates/goat-provider-kimi" }
goat-provider-qwen = { path = "crates/goat-provider-qwen" }
goat-provider-kimi-code = { path = "crates/goat-provider-kimi-code" }
goat-provider-xai = { path = "crates/goat-provider-xai" }
goat-provider-openai = { path = "crates/goat-provider-openai" }
goat-provider-openai-codex = { path = "crates/goat-provider-openai-codex" }
goat-provider-anthropic = { path = "crates/goat-provider-anthropic" }
Expand Down
16 changes: 16 additions & 0 deletions crates/goat-provider-deepseek/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "goat-provider-deepseek"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
publish = false

[dependencies]
goat-provider = { workspace = true }
goat-provider-openai-compat = { workspace = true }
goat-auth = { workspace = true }

[lints]
workspace = true
20 changes: 20 additions & 0 deletions crates/goat-provider-deepseek/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use goat_auth::CredentialStore;
use goat_provider_openai_compat::{OpenAiCompatProvider, api_key};

pub const PROVIDER_ID: &str = "deepseek";
const BASE_URL: &str = "https://api.deepseek.com";
const HOST: &str = "api.deepseek.com";
const ENV_VAR: &str = "DEEPSEEK_API_KEY";

const CATALOG: &[&str] = &["deepseek-chat", "deepseek-reasoner"];

const CONTEXT_WINDOWS: &[(&str, u32)] =
&[("deepseek-chat", 128_000), ("deepseek-reasoner", 128_000)];

pub fn build(store: &CredentialStore, account: &str) -> OpenAiCompatProvider {
api_key(store, account, PROVIDER_ID, BASE_URL, HOST, ENV_VAR)
.with_catalog(CATALOG)
.with_context_windows(CONTEXT_WINDOWS)
.with_images(false)
.with_reasoning_effort(false)
}
16 changes: 16 additions & 0 deletions crates/goat-provider-groq/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "goat-provider-groq"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
publish = false

[dependencies]
goat-provider = { workspace = true }
goat-provider-openai-compat = { workspace = true }
goat-auth = { workspace = true }

[lints]
workspace = true
39 changes: 39 additions & 0 deletions crates/goat-provider-groq/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use goat_auth::CredentialStore;
use goat_provider_openai_compat::{OpenAiCompatProvider, api_key};

pub const PROVIDER_ID: &str = "groq";
const BASE_URL: &str = "https://api.groq.com/openai/v1";
const HOST: &str = "api.groq.com";
const ENV_VAR: &str = "GROQ_API_KEY";

const CATALOG: &[&str] = &[
"openai/gpt-oss-120b",
"openai/gpt-oss-20b",
"moonshotai/kimi-k2-instruct-0905",
"qwen/qwen3-32b",
"deepseek-r1-distill-llama-70b",
"llama-3.3-70b-versatile",
];

const CONTEXT_WINDOWS: &[(&str, u32)] = &[
("openai/gpt-oss-120b", 131_072),
("openai/gpt-oss-20b", 131_072),
("moonshotai/kimi", 131_072),
("qwen/qwen3", 131_072),
("llama-3.3", 131_072),
];

fn is_chat_model(id: &str) -> bool {
let id = id.to_ascii_lowercase();
!(id.contains("whisper") || id.contains("tts") || id.contains("embedding"))
}

pub fn build(store: &CredentialStore, account: &str) -> OpenAiCompatProvider {
api_key(store, account, PROVIDER_ID, BASE_URL, HOST, ENV_VAR)
.with_catalog(CATALOG)
.with_context_windows(CONTEXT_WINDOWS)
.with_model_filter(is_chat_model)
.with_images(false)
.with_stream_options(false)
.with_reasoning_effort(false)
}
Loading
Loading