Skip to content

Commit ae8d69f

Browse files
feat: add Ollama Cloud as a declarative provider
Add ollama_cloud.json using the OpenAI-compatible engine with dynamic model discovery via /v1/models. Preserve oauth/device-code fields when rewriting declarative provider config keys. Co-developed-by: Michael Neale <michael@michaelneale.com>
1 parent dba12ba commit ae8d69f

6 files changed

Lines changed: 88 additions & 13 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "ollama_cloud",
3+
"engine": "openai",
4+
"display_name": "Ollama Cloud",
5+
"description": "Access hosted models on ollama.com via OpenAI-compatible API",
6+
"api_key_env": "OLLAMA_CLOUD_API_KEY",
7+
"base_url": "https://ollama.com/v1/chat/completions",
8+
"models": [],
9+
"dynamic_models": true,
10+
"headers": null,
11+
"timeout_seconds": null,
12+
"supports_streaming": true
13+
}

crates/goose/src/providers/provider_registry.rs

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,20 +112,22 @@ impl ProviderRegistry {
112112
if !config.requires_auth {
113113
config_keys.remove(api_key_index);
114114
} else if !config.api_key_env.is_empty() {
115+
let original = &config_keys[api_key_index];
115116
let api_key_required = provider_type == ProviderType::Declarative;
116-
config_keys[api_key_index] = super::base::ConfigKey::new(
117-
&config.api_key_env,
118-
api_key_required,
119-
true,
120-
None,
121-
true,
122-
);
117+
config_keys[api_key_index] = super::base::ConfigKey {
118+
name: config.api_key_env.clone(),
119+
required: api_key_required,
120+
secret: true,
121+
default: None,
122+
oauth_flow: original.oauth_flow,
123+
device_code_flow: original.device_code_flow,
124+
primary: original.primary,
125+
};
123126
}
124127
}
125128

126129
if let Some(ref env_vars) = config.env_vars {
127130
for ev in env_vars {
128-
// Default primary to `required` so required fields show prominently in the UI
129131
let primary = ev.primary.unwrap_or(ev.required);
130132
config_keys.push(super::base::ConfigKey::new(
131133
&ev.name,
@@ -204,3 +206,54 @@ impl ProviderRegistry {
204206
self.entries.retain(|name, _| !name.starts_with("custom_"));
205207
}
206208
}
209+
210+
#[cfg(test)]
211+
mod tests {
212+
use super::*;
213+
use crate::config::declarative_providers::ProviderEngine;
214+
use crate::providers::openai::OpenAiProvider;
215+
216+
#[test]
217+
fn register_with_name_rewrites_secret_key_for_declarative_provider() {
218+
let mut registry = ProviderRegistry::new();
219+
let config = DeclarativeProviderConfig {
220+
name: "ollama_cloud".to_string(),
221+
engine: ProviderEngine::OpenAI,
222+
display_name: "Ollama Cloud".to_string(),
223+
description: None,
224+
api_key_env: "OLLAMA_CLOUD_API_KEY".to_string(),
225+
base_url: "https://ollama.com/v1/chat/completions".to_string(),
226+
models: vec![],
227+
headers: None,
228+
timeout_seconds: None,
229+
supports_streaming: Some(true),
230+
requires_auth: true,
231+
catalog_provider_id: None,
232+
base_path: None,
233+
env_vars: None,
234+
dynamic_models: None,
235+
skip_canonical_filtering: false,
236+
fast_model: None,
237+
};
238+
239+
registry.register_with_name::<OpenAiProvider, _>(
240+
&config,
241+
ProviderType::Declarative,
242+
|_model| unreachable!("constructor is not used in metadata tests"),
243+
);
244+
245+
let entry = registry
246+
.entries
247+
.get("ollama_cloud")
248+
.expect("declarative provider should be registered");
249+
let api_key = entry
250+
.metadata
251+
.config_keys
252+
.iter()
253+
.find(|key| key.secret)
254+
.expect("declarative provider should expose a secret config key");
255+
256+
assert_eq!(api_key.name, "OLLAMA_CLOUD_API_KEY");
257+
assert!(api_key.required);
258+
}
259+
}

documentation/docs/getting-started/providers.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ goose is compatible with a wide range of LLM providers, allowing you to choose a
3838
| [LM Studio](https://lmstudio.ai/) | Run local models with LM Studio's OpenAI-compatible server. **Because this provider runs locally, you must first [download a model](#local-llms).** | None required. Connects to local server at `localhost:1234` by default. |
3939
| [Mistral AI](https://mistral.ai/) | Provides access to Mistral models including general-purpose models, specialized coding models (Codestral), and multimodal models (Pixtral). | `MISTRAL_API_KEY` |
4040
| [Ollama](https://ollama.com/) | Local model runner supporting Qwen, Llama, DeepSeek, and other open-source models. **Because this provider runs locally, you must first [download and run a model](#local-llms).** | `OLLAMA_HOST` |
41+
| [Ollama Cloud](https://ollama.com/) | Access hosted models on ollama.com via OpenAI-compatible API. Requires an Ollama account and API key. | `OLLAMA_CLOUD_API_KEY` |
4142
| [OpenAI](https://platform.openai.com/api-keys) | Provides gpt-4o, o1, and other advanced language models. Also supports OpenAI-compatible endpoints (e.g., self-hosted LLaMA, vLLM, KServe). **o1-mini and o1-preview are not supported because goose uses tool calling.** | `OPENAI_API_KEY`, `OPENAI_HOST` (optional), `OPENAI_ORGANIZATION` (optional), `OPENAI_PROJECT` (optional), `OPENAI_CUSTOM_HEADERS` (optional) |
4243
| [OpenRouter](https://openrouter.ai/) | API gateway for unified access to various models with features like rate-limiting management. | `OPENROUTER_API_KEY` |
4344
| [OVHcloud AI](https://www.ovhcloud.com/en/public-cloud/ai-endpoints/) | Provides access to open-source models including Qwen, Llama, Mistral, and DeepSeek through AI Endpoints service. | `OVHCLOUD_API_KEY` |
@@ -993,9 +994,10 @@ Here are some local providers we support:
993994
6. Enter the host where your model is running
994995

995996
:::info Endpoint
996-
For Ollama, if you don't provide a host, we set it to `localhost:11434`.
997-
When constructing the URL, we prepend `http://` if the scheme is not `http` or `https`.
997+
For Ollama, if you don't provide a host, we set it to `localhost:11434`.
998+
When constructing the URL, we prepend `http://` if the scheme is not `http` or `https`.
998999
If you're running Ollama on a different server, you'll have to set `OLLAMA_HOST=http://{host}:{port}`.
1000+
For hosted models on ollama.com, use the **Ollama Cloud** provider instead.
9991001
:::
10001002

10011003
```

documentation/docs/guides/context-engineering/using-goosehints.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ my-project/
139139
```
140140

141141
If you start goose in `my-project/`, the root-level hints are loaded immediately. Later, when goose accesses files under `frontend/components/`, it loads the nested hints for that path and combines them in the following order:
142+
142143
1. <details>
143144
<summary>`my-project/.goosehints` (project root)</summary>
144145
```
@@ -151,8 +152,6 @@ If you start goose in `my-project/`, the root-level hints are loaded immediately
151152
Use conventional commits for all changes.
152153
```
153154
</details>
154-
155-
After nested hints are loaded for a directory, they remain active for the rest of the session. If you update a hint file and want goose to pick up the new content reliably, restart the session.
156155
2. <details>
157156
<summary>`frontend/.goosehints`</summary>
158157
```
@@ -195,6 +194,10 @@ After nested hints are loaded for a directory, they remain active for the rest o
195194
```
196195
</details>
197196

197+
:::note
198+
After nested hints are loaded for a directory, they remain active for the rest of the session. If you update a hint file and want goose to pick up the new content reliably, restart the session.
199+
:::
200+
198201
## Common Use Cases
199202
Here are some ways people have used hints to provide additional context to goose:
200203

ui/desktop/src/components/settings/providers/subcomponents/utils/StringUtils.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ const i18n = defineMessages({
1212
},
1313
ollamaNotConfiguredSuffix: {
1414
id: 'stringUtils.ollamaNotConfiguredSuffix',
15-
defaultMessage: 'must be installed on your machine and open, or you must enter a value for OLLAMA_HOST.',
15+
defaultMessage:
16+
'must be installed on your machine and open, or you must enter a value for OLLAMA_HOST.',
1617
},
1718
configuredProvider: {
1819
id: 'stringUtils.configuredProvider',

ui/desktop/src/utils/configUtils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ export const configLabels: Record<string, string> = {
4444
// ollama
4545
OLLAMA_HOST: 'Ollama Host',
4646

47+
// ollama cloud
48+
OLLAMA_CLOUD_API_KEY: 'Ollama Cloud API Key',
49+
4750
// azure openai
4851
AZURE_OPENAI_API_KEY: 'Azure OpenAI API Key',
4952
AZURE_OPENAI_ENDPOINT: 'Azure OpenAI Endpoint',

0 commit comments

Comments
 (0)