diff --git a/docs/advanced/environment-variables.md b/docs/advanced/environment-variables.md index 88b1d54..0974871 100644 --- a/docs/advanced/environment-variables.md +++ b/docs/advanced/environment-variables.md @@ -214,14 +214,14 @@ See [cost tracking](./cost-tracking.md). ## LLM provider and routing -See [LLM providers](./llm-providers.md) and the [LiteLLM backend](./litellm-backend.md). With nothing here or in a provider key set, a non-interactive run picks a provider itself; see [When nothing is configured](./llm-providers.md#when-nothing-is-configured). *On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* +See [LLM providers](./llm-providers.md) and the [LiteLLM backend](./litellm-backend.md). *On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* With nothing here or in a provider key set, a non-interactive run picks a provider itself; see [When nothing is configured](./llm-providers.md#when-nothing-is-configured). *On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* When nothing names a provider and none is found, forge stops with `copilot_no_llm_provider`. | Variable | Effect | |---|---| | `FLUID_LLM_PROVIDER` | Default provider. | | `FLUID_LLM_MODEL` | Default model. | -| `FLUID_LLM_API_KEY` | API key used when no provider-specific key is set. | -| `FLUID_LLM_ENDPOINT` | Overrides the model endpoint. The routing model's calls use it too, unless `FLUID_LLM_ROUTING_ENDPOINT` names another. *On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* | +| `FLUID_LLM_API_KEY` | API key for the selected provider. It wins over the provider's own key variable, such as `OPENAI_API_KEY`. It does not choose the provider: name one with `--llm-provider`, `FLUID_LLM_PROVIDER` or the key of exactly one provider. *On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* With none named, forge stops with `copilot_no_llm_provider`; before #852 it sent the key to `gemini`. | +| `FLUID_LLM_ENDPOINT` | Overrides the model endpoint. *On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* The routing model's calls use it too, unless `FLUID_LLM_ROUTING_ENDPOINT` names another. *On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* For Ollama, forge's model pick and model check ask it too, and each `--tiered` stage calls it. | | `FLUID_LLM_TIMEOUT_SECONDS` | Per-call timeout. Default `120`. | | `FLUID_LLM_STREAMING` | Default on; `0`, `false`, `no` or `off` disables streaming. | | `FLUID_LLM_ROUTING_ENDPOINT`, `FLUID_LLM_ROUTING_MODEL` | Optional routing endpoint and model. | @@ -297,7 +297,7 @@ Read by `fluid mcp output-port`; see [MCP](./mcp.md). | `FLUID_STORE_VECTOR_BACKING` | Backing store of the `vector` backend: `file` (default), `sqlite` or `postgres`. | | `FLUID_SECRETS_INMEMORY` | `1` keeps `fluid secrets` values in memory instead of the OS keychain. Tests only. | | `FLUID_ENCRYPTION_KEY`, `FLUID_ENCRYPTION_PASSPHRASE` | Fernet key, or a passphrase it is derived from, for the encrypted credential store. | -| `FLUID_ALLOW_PLAINTEXT_AI_SECRETS` | Legacy plaintext fallback for AI provider secrets. | +| `FLUID_ALLOW_PLAINTEXT_AI_SECRETS` | Legacy plaintext fallback for AI provider secrets. *On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* It also lets `fluid ai setup` save a `user:password@` in an address, such as an Ollama host, to `~/.fluid/ai_config.json`; without it the address is saved without them. | | `FLUID_ALLOW_PLAINTEXT_SOURCE_SECRETS` | Legacy plaintext secrets in `sources.yaml`; the file must also be mode 600. | ## Marketplace diff --git a/docs/advanced/error-codes.md b/docs/advanced/error-codes.md index 38a78a0..da2f801 100644 --- a/docs/advanced/error-codes.md +++ b/docs/advanced/error-codes.md @@ -378,6 +378,15 @@ The `fluid generate iac` and `fluid apply` runs print the refusal and exit befor - For Ollama, start the local server and pull the requested model - Pass --llm-model with a known-available model +### copilot_no_llm_provider + +*On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* `ERR_COPILOT_NO_LLM_PROVIDER`. The documentation link lands on [Production troubleshooting](./production-troubleshooting.md), where [No LLM provider is selected](./production-troubleshooting.md#no-llm-provider-is-selected) explains each message. + +- Pass --llm-provider, for example anthropic, openai, gemini or ollama +- Or set FLUID_LLM_PROVIDER, or the API key of exactly one provider +- Or run 'fluid ai setup' to choose and save a provider +- Run 'fluid doctor' to see what forge would select + ### model_not_found `ERR_MODEL_NOT_FOUND`. The documentation link lands on [Production troubleshooting](./production-troubleshooting.md). diff --git a/docs/advanced/litellm-backend.md b/docs/advanced/litellm-backend.md index 91c7b1c..950714b 100644 --- a/docs/advanced/litellm-backend.md +++ b/docs/advanced/litellm-backend.md @@ -63,7 +63,7 @@ If you point LiteLLM at a model the catalog doesn't know, the run-start banner s ## Caveats - Tool-use behaviour matches LiteLLM's wrapper. Provider failures surface as the typed errors described in [Typed Errors](./typed-errors.md) (`RateLimitError`, `ContextOverflowError` and the others). -- Forge's Ollama calls go to `OLLAMA_HOST` whatever its host; only the probe for a running Ollama, model discovery and the interactive Ollama setup stay on `localhost`. See [The Ollama endpoint](./llm-providers.md#the-ollama-endpoint). *On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* +- *On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* Forge's Ollama calls go to `OLLAMA_HOST` whatever its host. See [The Ollama endpoint](./llm-providers.md#the-ollama-endpoint). *On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* The probe for a running Ollama, model discovery and the interactive Ollama setup follow it too; before #852 they stayed on `localhost`. ## See also diff --git a/docs/advanced/llm-providers.md b/docs/advanced/llm-providers.md index 540daef..945ff71 100644 --- a/docs/advanced/llm-providers.md +++ b/docs/advanced/llm-providers.md @@ -61,7 +61,7 @@ When it picks an agent, forge logs a warning that names it: No LLM provider is configured; using claude-code, the first coding agent found on PATH. Override with --llm-provider. ``` -With neither, the run falls back to `gemini`, which needs a Gemini key and the `litellm` extra. Through 0.28.0 a run with no Ollama running always fell back to `gemini`, even with a signed-in Claude Code on `PATH`. +With neither, the run stops with `copilot_no_llm_provider` and says why; see [No LLM provider is selected](./production-troubleshooting.md#no-llm-provider-is-selected). *On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* Before #852 it fell back to `gemini`, which needs a Gemini key and the `litellm` extra. Through 0.28.0 it did so whenever no Ollama was running, even with a signed-in Claude Code on `PATH`. Any of these configures an LLM, and then no agent is picked: @@ -70,12 +70,14 @@ Any of these configures an LLM, and then no agent is picked: - `FLUID_LLM_API_KEY`, `FLUID_LLM_ENDPOINT`, `FLUID_LLM_MODEL`, `FLUID_LLM_ROUTING_ENDPOINT`, `FLUID_LLM_ROUTING_MODEL` or `OLLAMA_HOST`. - A provider key: `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, `GOOGLE_API_KEY` or `GITHUB_API_KEY`. - An OpenAI, Anthropic or Gemini key in the OS keyring. -- A provider saved with `fluid ai setup`. A non-interactive run does not use the saved provider itself, so name it with `--llm-provider` or `FLUID_LLM_PROVIDER`. +- A provider saved with `fluid ai setup`. A run with `--non-interactive`, which `--agent` sets, does not use the saved provider itself, so name it with `--llm-provider` or `FLUID_LLM_PROVIDER`. - The agent loop: `--agent-loop`, or `FLUID_COPILOT_AGENT_LOOP` set to `1`, `true`, `yes` or `on`. The loop needs tool use, which these agents do not offer. +*On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* Only some of these name a provider: `--llm-provider`, `FLUID_LLM_PROVIDER` and `FLUID_FORGE_AGENT`; the key of exactly one of OpenAI, Anthropic, Gemini (`GEMINI_API_KEY` or `GOOGLE_API_KEY`) and Ollama (`OLLAMA_HOST`); a keyring key for exactly one provider; and a provider saved with `fluid ai setup`, in a run without `--non-interactive`. The others, `GITHUB_API_KEY` among them, only stop forge from picking an agent: an Ollama that answers at `OLLAMA_HOST` or `localhost:11434` is still used. When nothing names a provider and no Ollama answers, forge stops with `copilot_no_llm_provider`. So pass `--llm-provider` together with `--llm-endpoint`, `--llm-model` or `FLUID_LLM_API_KEY`. + The pick works on the base install, because the agent CLIs need no `litellm` extra. A running local Ollama still comes first there, so that run asks for the extra instead of sending the prompt to an agent. -An interactive run does not pick an agent: `fluid forge` in a terminal offers Claude Code in its inline setup instead. With nothing configured, [`fluid doctor`](../cli/doctor.md) run without a terminal reports the provider this pick chooses. When the pick finds nothing, its Provider row says `Not selected`. +An interactive run does not pick an agent: `fluid forge` in a terminal offers Claude Code in its inline setup instead. With nothing configured, [`fluid doctor`](../cli/doctor.md) run without a terminal reports the provider this pick chooses. When the pick finds nothing, its Provider row says `Not selected`. *On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* `fluid doctor --verbose` then shows, in its Message row, the reason forge would stop with. ## Claude Code @@ -129,12 +131,27 @@ Forge reads `OLLAMA_HOST` the way Ollama's own Python client does ([`_parse_host | `https://gpu-box` | `https://gpu-box:443` | | `https://gpu-box/ollama` | `https://gpu-box:443/ollama` | -The host can be another machine. Model discovery still looks only at a local Ollama: with no model named, forge takes the largest model pulled on `localhost`, else `gemma4:latest`. With a remote `OLLAMA_HOST`, name the model with `--llm-model` or `FLUID_LLM_MODEL`. Forge's local-only checks print `OLLAMA_HOST points to a non-localhost address (...), ignoring.` for such a host. The call still goes to `OLLAMA_HOST`. - -A stated endpoint is used for the routing model's calls too, unless `--llm-routing-endpoint` or `FLUID_LLM_ROUTING_ENDPOINT` names another. A project's `.env` cannot set `OLLAMA_HOST`; see [Project dotenv trust](./project-dotenv-trust.md). +The host can be another machine. A stated endpoint is used for the routing model's calls too, unless `--llm-routing-endpoint` or `FLUID_LLM_ROUTING_ENDPOINT` names another. A project's `.env` cannot set `OLLAMA_HOST`; see [Project dotenv trust](./project-dotenv-trust.md). Through 0.28.0 these calls went to a `litellm://ollama/` label unless an endpoint was stated, so they never reached the daemon. +### One address for every Ollama read + +*On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* + +Every Ollama read takes any host, as Ollama's own client does: + +- With no `--llm-model`, forge asks the daemon the call goes to, and uses its largest model, else `gemma4:latest`. +- The model check that `--require-llm`, `--tiered` and `FLUID_LLM_MODEL_PREFLIGHT` turn on asks that daemon too. The model check of `fluid ai test` asks its `--endpoint`, else `OLLAMA_HOST`, else `localhost:11434`. +- Each `--tiered` stage calls a stated endpoint, as the routing model does. +- The probe that finds a running Ollama, and `fluid ai setup`, read `OLLAMA_HOST`, else `localhost:11434`. A stated endpoint does not reach them. + +Forge reads `--llm-endpoint` and `FLUID_LLM_ENDPOINT` the same way as `OLLAMA_HOST`: `--llm-endpoint gpu-box` means `http://gpu-box:11434`, and the scheme may be in upper or lower case. `fluid ai test --endpoint` takes only an absolute `http` or `https` URL. A stated endpoint does not choose Ollama, so pass `--llm-provider ollama` with it. `OLLAMA_HOST` on its own does choose it. + +An address may carry `user:password@`, for an Ollama behind a proxy that asks for basic auth, and forge sends it as basic auth, to the call and to the model lookups. `fluid ai test --endpoint` refuses one. `fluid doctor` and `fluid ai setup` show it as `***:***@`. `fluid ai setup` saves the address to `~/.fluid/ai_config.json` without it, as it does for API keys, unless `FLUID_ALLOW_PLAINTEXT_AI_SECRETS` is set, and logs a warning to keep the credentials in the environment. An `OLLAMA_HOST` in the environment wins over the saved address, so keep them there. + +Before #852, forge's default-model pick, the probe and `fluid ai setup` looked only at `localhost`. They ignored a non-local `OLLAMA_HOST` with the warning `OLLAMA_HOST points to a non-localhost address (...), ignoring.`, so the model could come from one daemon while the call went to another. The model checks read `OLLAMA_HOST`, never a stated endpoint, and a `--tiered` stage called the provider's default address. A stated endpoint was used exactly as given, so its `user:password@` reached the call but no lookup, and `OLLAMA_HOST` lost its `user:password@`. + ## Tiered mode `--tiered` chooses different models within the same provider, never across providers. A typical layout is: diff --git a/docs/advanced/network-safety.md b/docs/advanced/network-safety.md index 9d9f7b3..81045a6 100644 --- a/docs/advanced/network-safety.md +++ b/docs/advanced/network-safety.md @@ -13,7 +13,7 @@ This page describes the user-visible behaviour: which flags opt in, which enviro | `fluid odps import ` | Followed `http(s)` `contractId` references | Local only; remote references are rejected | `--allow-remote` | | `BitolOdpsProvider().import_contract(...)` (Python) | `allow_remote=True` | `allow_remote=False` | `allow_remote=True` (keyword) | | `ContractResolver(...)` (Python) | `allow_remote=True` | `allow_remote=False` | `allow_remote=True` (keyword) | -| Ollama endpoint | Configurable | Localhost only | None, by design. *On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* Calls now go to `OLLAMA_HOST` whatever the host; see [Ollama and `OLLAMA_HOST`](#ollama-and-ollama-host). | +| Ollama endpoint | Configurable | Localhost only | None, by design. *On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* Calls now go to `OLLAMA_HOST` whatever the host. *On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* So do the probe, model discovery and the interactive setup; see [Ollama and `OLLAMA_HOST`](#ollama-and-ollama-host). | | HTTP redirects on a safe-HTTP client | Followed | Not followed | `follow_redirects=True` (per client) | The CLI flags `--no-remote` and `--seed-no-remote` remain as hidden aliases that do nothing, so older scripts keep working. @@ -70,7 +70,9 @@ As of 0.18.1, nothing in the CLI reads `FLUID_SAFE_MODE` or acts on the global ` ## Ollama and `OLLAMA_HOST` -*On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* Forge's Ollama calls go to `--llm-endpoint` or `FLUID_LLM_ENDPOINT`, else to `OLLAMA_HOST` whatever its host, else to `http://localhost:11434`; see [The Ollama endpoint](./llm-providers.md#the-ollama-endpoint). Three things still look only at `localhost`, and ignore a non-local `OLLAMA_HOST` with a warning: the probe that finds a running Ollama, model discovery, and the interactive Ollama setup. A project's `.env` cannot set `OLLAMA_HOST` ([Project dotenv trust](./project-dotenv-trust.md)). +*On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* Forge's Ollama calls go to `--llm-endpoint` or `FLUID_LLM_ENDPOINT`, else to `OLLAMA_HOST` whatever its host, else to `http://localhost:11434`; see [The Ollama endpoint](./llm-providers.md#the-ollama-endpoint). A project's `.env` cannot set `OLLAMA_HOST` ([Project dotenv trust](./project-dotenv-trust.md)). + +*On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* No Ollama read is held to `localhost` any more. The probe that finds a running Ollama, model discovery and the interactive Ollama setup follow `OLLAMA_HOST` too, and `fluid ai test --endpoint` takes an Ollama address on any host, over `http` or `https`; it still refuses a `user:password@` in the URL. Before #852 those three looked only at `localhost` and ignored a non-local `OLLAMA_HOST` with a warning, and `fluid ai test` refused an Ollama endpoint that was not `http` on `localhost`. [One address for every Ollama read](./llm-providers.md#one-address-for-every-ollama-read) also covers a `user:password@` in forge's own Ollama address. Through 0.28.0 the calls went to a `litellm://ollama/` label unless an endpoint was stated, so no `OLLAMA_HOST`, local or remote, reached a daemon. diff --git a/docs/advanced/production-troubleshooting.md b/docs/advanced/production-troubleshooting.md index 32eff2b..bb248f1 100644 --- a/docs/advanced/production-troubleshooting.md +++ b/docs/advanced/production-troubleshooting.md @@ -252,7 +252,29 @@ fluid ai setup # re-run setup to store the rotated key Update the key at the source `fluid ai status` reports, the provider environment variable (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`) or the stored configuration, then confirm with `fluid ai test`. See [LLM providers and backends](./llm-providers.md) for the resolution order. -[`fluid ai test`](../cli/ai.md#ai-test) is the connectivity check. As of 0.18.1, `fluid ai test` with no provider configured and no `--provider` fails with `❌ Unexpected error: name 'detect_ollama_available' is not defined` and exit code 2. Name the provider, or run `fluid ai setup` first. `fluid ai test --provider --json` prints a report with `ok`, `exit_code` and, on failure, an `error` object with `code`, `message` and `suggestions`. +[`fluid ai test`](../cli/ai.md#ai-test) is the connectivity check. With no provider configured and no `--provider`, it exits 2 with `ai_test_no_provider`; name the provider, or run `fluid ai setup` first. *(since 0.28.0, [forge-cli #839](https://github.com/Agenticstiger/forge-cli/pull/839))* Through 0.27.0 it failed with `❌ Unexpected error: name 'detect_ollama_available' is not defined` instead. `fluid ai test --provider --json` prints a report with `ok`, `exit_code` and, on failure, an `error` object with `code`, `message` and `suggestions`. + +### No LLM provider is selected + +*On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* `copilot_no_llm_provider` means that nothing named an LLM provider and no Ollama answered at `OLLAMA_HOST` or `localhost:11434`, so forge stopped instead of guessing one. Its first line says why: + +| Message | Cause | +| --- | --- | +| `No LLM provider configured. Run 'fluid ai setup' or set an API key env var.` | Nothing names a provider. A run without a terminal, or with `--non-interactive`, also found no coding-agent CLI on `PATH`, or a setting that names none, such as `--llm-model` or `--llm-endpoint`, kept it from looking for one. Keys in the OS keyring for more than one provider give this message too. See [When nothing is configured](./llm-providers.md#when-nothing-is-configured). | +| `No LLM provider is selected: OPENAI_API_KEY, ANTHROPIC_API_KEY are all set, so none of them is chosen. Pick one with --llm-provider or FLUID_LLM_PROVIDER.` | More than one provider's key is set in the environment. `OLLAMA_HOST` counts as Ollama's. | +| `No LLM provider is selected: FLUID_LLM_API_KEY does not say which provider the key is for. Pick one with --llm-provider or FLUID_LLM_PROVIDER.` | `FLUID_LLM_API_KEY` is set, but nothing names a provider. | +| `No LLM provider is selected for this run: 'fluid ai setup' saved anthropic, but this command does not read the saved choice. Pass --llm-provider anthropic.` | The command does not read the provider `fluid ai setup` saved: `fluid forge data-model`, or `fluid forge` with `--non-interactive`, which `--agent` sets. | + +The suggestions under it are the same in each case: + +```text +• Pass --llm-provider, for example anthropic, openai, gemini or ollama +• Or set FLUID_LLM_PROVIDER, or the API key of exactly one provider +• Or run 'fluid ai setup' to choose and save a provider +• Run 'fluid doctor' to see what forge would select +``` + +`fluid doctor --verbose` shows the same reason in its Message row, except for a saved provider, which `fluid doctor` reads. Before #852 forge used `gemini` in each case: it sent `FLUID_LLM_API_KEY` to Google whatever key it was, and with two provider keys set it asked for a Gemini key. ### A coding agent that is not signed in diff --git a/docs/cli/ai.md b/docs/cli/ai.md index 821ad9d..13d10c4 100644 --- a/docs/cli/ai.md +++ b/docs/cli/ai.md @@ -73,7 +73,7 @@ Sends a small request to the configured provider and reports whether it answered | --- | --- | | `--provider {gemini,openai,anthropic,claude,ollama}` | Test this provider instead of the configured one. | | `--model ID` | Override the model for this test. | -| `--endpoint URL` | Override the endpoint. A cloud endpoint must be HTTPS, so an API key is never sent in plaintext. | +| `--endpoint URL` | Override the endpoint: an absolute `http` or `https` URL without `user:password@`. A cloud endpoint must be HTTPS, so an API key is never sent in plaintext. An Ollama endpoint may be on any host, over `http` or `https`. *On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* Before #852 an Ollama endpoint had to be `http` on `localhost`. | | `--timeout-seconds N` | HTTP timeout for the request. | | `--json` | Print a stable JSON report instead of the table. | @@ -108,23 +108,21 @@ fluid ai test --provider ollama --endpoint http://127.0.0.1:9 --timeout-seconds } ``` -The model check before the call lists the models of `OLLAMA_HOST`, else of `localhost:11434`, not those at `--endpoint`. With a local Ollama running, the example above therefore exits `4` with `ai_test_model_unavailable` when that Ollama does not have the model. +*On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* The model check before the call asks the address the call goes to: `--endpoint`, else `OLLAMA_HOST`, else `localhost:11434`. An address that does not answer gives no model list, so the example above goes on to the call and exits `5`. Before #852 the check ignored `--endpoint`: with a local Ollama running, the example exited `4` with `ai_test_model_unavailable` when that Ollama did not have the model. The process exit code equals `exit_code`: | Code | Meaning | | --- | --- | | `0` | The provider answered. | -| `2` | No usable configuration, or a cloud endpoint that is not HTTPS. | +| `2` | No usable configuration, or an `--endpoint` it refuses: one with `user:password@`, one that is not an absolute `http` or `https` URL, or a cloud endpoint that is not HTTPS. | | `3` | The provider rejected the credentials. | | `4` | The request failed in another way, for example an unknown model. | | `5` | A network failure, a `429` or a `5xx`. | *On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* A `401` or `403` from the call exits `3`. A model the provider does not have exits `4`: it is missing from the provider's model list (`ai_test_model_unavailable`), or the call returns a `404`. A reply without `FLUID_OK` exits `4` too (`ai_test_unexpected_response`). Any other failure of the call, such as a refused connection, a timeout, a `429` or a `5xx`, exits `5` with `"code": "copilot_litellm_request_failed"` and a message that starts `litellm request failed:`. The failed run above reads that way on `main`, in place of `ai_test_network_error`. -::: warning `fluid ai test` crashes with no provider configured -As of 0.18.1, on a machine with no provider configured and no key in the environment, `fluid ai test` and `fluid ai test --json` stop with `Unexpected error: name 'detect_ollama_available' is not defined` and exit `2`, instead of the JSON report with `ai_test_no_provider`. Run `fluid ai setup` first, or pass `--provider` with a configured provider. -::: +With no provider configured and no key in the environment, `fluid ai test` exits `2` with `ai_test_no_provider` and the message `No AI provider configured. Run 'fluid ai setup' or set a provider API key.` *(since 0.28.0, [forge-cli #839](https://github.com/Agenticstiger/forge-cli/pull/839))* Through 0.27.0, `fluid ai test` and `fluid ai test --json` stopped with `Unexpected error: name 'detect_ollama_available' is not defined` instead, also with exit `2`. ### `ai models` @@ -149,8 +147,8 @@ fluid ai models --json | jq '.gemini.tier_models' - `setup` is interactive and requires a TTY with `rich` installed. It walks through provider choice (Google Gemini, OpenAI, Anthropic, Ollama, or skip), validates the API key with a lightweight call, and persists settings. - Provider config is written to `~/.fluid/ai_config.json` (chmod 600). API keys are stored in the OS keyring when available and are not written to the JSON file by default. - If no keyring backend is available, the API key is kept only for the current process. Set `FLUID_ALLOW_PLAINTEXT_AI_SECRETS=1` only when you deliberately want plaintext local persistence. -- The interactive Ollama setup uses only a local Ollama: a non-local `OLLAMA_HOST` is ignored there, with a warning, and replaced with `http://localhost:11434`. *On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* Forge's own Ollama calls, and `fluid ai test`, go to `OLLAMA_HOST` whatever the host; see [The Ollama endpoint](../advanced/llm-providers.md#the-ollama-endpoint). -- The same setup flow is invoked inline on first use of [`fluid forge`](./forge.md) in a terminal when no provider is configured. *On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* A run without a terminal picks a provider itself instead; see [When nothing is configured](../advanced/llm-providers.md#when-nothing-is-configured). +- *On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* The interactive Ollama setup uses `OLLAMA_HOST` whatever its host, else `http://localhost:11434`. It shows a `user:password@` in the address as `***:***@`, and saves the address without it unless `FLUID_ALLOW_PLAINTEXT_AI_SECRETS` is set. Before #852 it replaced a non-local `OLLAMA_HOST` with `http://localhost:11434`, with a warning. See [One address for every Ollama read](../advanced/llm-providers.md#one-address-for-every-ollama-read). *On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* Forge's own Ollama calls, and `fluid ai test`, go to `OLLAMA_HOST` whatever the host; see [The Ollama endpoint](../advanced/llm-providers.md#the-ollama-endpoint). +- The same setup flow is invoked inline on first use of [`fluid forge`](./forge.md) in a terminal when no provider is configured. *On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* A run without a terminal picks a provider itself instead; see [When nothing is configured](../advanced/llm-providers.md#when-nothing-is-configured). *On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* With no provider named or found, such a run stops with `copilot_no_llm_provider`. - Provider environment variables (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, etc.) are respected as a fallback. - For strict provider validation, run `fluid forge data-model from-intent ... --require-llm` so provider failures do not silently fall back to heuristics. - For tiered per-stage model selection, pass `--tiered` on the forge command. Tiering stays within the active provider and collapses to single-model mode when the provider has no distinct tier catalog. diff --git a/docs/cli/doctor.md b/docs/cli/doctor.md index 4383179..cead616 100644 --- a/docs/cli/doctor.md +++ b/docs/cli/doctor.md @@ -64,7 +64,7 @@ fluid doctor --env `fluid doctor` with neither `--scope` nor `--env` runs the default checks: -- **Forge copilot readiness**: whether an LLM provider and model are configured, and whether credentials are present. *On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* Its Endpoint row says `Provider default` when no endpoint is set and the provider picks its own address; through 0.28.0 it showed a `litellm:///` label. A ready coding agent's row says `coding-agent://`. With `FLUID_LLM_PROVIDER` set to `claude-code`, `codex`, `cursor` or `kiro`, the copilot is ready when the agent's CLI (`claude`, `codex`, `cursor-agent` or `kiro-cli`) is on `PATH`; the CLI's own sign-in is not checked. Without the CLI, the table says Setup needed and the panel names it: `The 'cursor-agent' CLI for cursor is not on PATH.` Through 0.28.0 these providers read "No API key found". With nothing configured and no terminal, readiness reports the provider a non-interactive forge run picks, or `Not selected` when that pick finds nothing (forge itself then falls back to `gemini`); see [When nothing is configured](../advanced/llm-providers.md#when-nothing-is-configured). +- **Forge copilot readiness**: whether an LLM provider and model are configured, and whether credentials are present. *On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* Its Endpoint row says `Provider default` when no endpoint is set and the provider picks its own address; through 0.28.0 it showed a `litellm:///` label. A ready coding agent's row says `coding-agent://`. With `FLUID_LLM_PROVIDER` set to `claude-code`, `codex`, `cursor` or `kiro`, the copilot is ready when the agent's CLI (`claude`, `codex`, `cursor-agent` or `kiro-cli`) is on `PATH`; the CLI's own sign-in is not checked. Without the CLI, the table says Setup needed and the panel names it: `The 'cursor-agent' CLI for cursor is not on PATH.` Through 0.28.0 these providers read "No API key found". With nothing configured and no terminal, readiness reports the provider a non-interactive forge run picks, or `Not selected` when that pick finds nothing; see [When nothing is configured](../advanced/llm-providers.md#when-nothing-is-configured). *On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* With no provider selected, the Message row that `--verbose` shows gives the same reason as forge's `copilot_no_llm_provider`, for example `No LLM provider is selected: OPENAI_API_KEY, ANTHROPIC_API_KEY are all set, so none of them is chosen. Pick one with --llm-provider or FLUID_LLM_PROVIDER.` Before #852 the row always read `No LLM provider configured. Run 'fluid ai setup' or set an API key env var.`, and forge fell back to `gemini` where readiness said `Not selected`. A `user:password@` in the endpoint shows as `***:***@`. - **Memory store backend**: which backend holds copilot memory (`file`, `sqlite`, `postgres` or `vector`), where it lives, and whether it is readable and writable. - **Provider readiness**: under `--verbose`, a **Databricks Provider** row reports `Not configured` when no `DATABRICKS_*` workspace variable is set, and otherwise whether the credential resolves under the same rules `fluid apply` uses, whether `DATABRICKS_WAREHOUSE_ID` is set and whether `databricks-sdk` is installed. It never opens a connection and never changes the exit code. See [Databricks](../providers/databricks.md#fluid-doctor-and-the-welcome-scan). - **Feature availability**: a table of CLI components (the schema manager, the sovereignty and agent-policy validators, the provider action parsers for GCP, AWS and Snowflake, the dbt engine, the copilot, the LiteLLM backend and the agent web tools), each marked available, not available or not installed. `--features-only` prints just this table. diff --git a/docs/cli/forge.md b/docs/cli/forge.md index c7c9e3d..6f3fe8d 100644 --- a/docs/cli/forge.md +++ b/docs/cli/forge.md @@ -2,7 +2,7 @@ Author a data product contract with an AI copilot, an offline guided interview, or an empty skeleton. By default forge writes `contract.fluid.yaml` and `.fluid/forge-receipt.json`; a scaffold (`--scaffold`), a CI pipeline (`--ci`) and engine artifacts add files. -AI forge with an API-key provider needs the `litellm` extra (alias `ai`): `pip install 'data-product-forge[litellm]'`. *(since 0.28.0, [forge-cli #837](https://github.com/Agenticstiger/forge-cli/pull/837))* Without it, an explicit `--llm-provider` that names an API-key provider (`openai`, `anthropic`, `gemini`, `ollama`), on `fluid forge`, on `fluid forge data-model` or with `--agent-loop`, prints that line and exits 1 with `[ERR_COPILOT_LITELLM_UNAVAILABLE]`, before the interview reads any context or memory. `fluid forge data-model` with no `--llm-provider`, `--llm-model` or `--llm-endpoint` runs offline and needs no extra (`--llm-model` alone resolves to the provider saved with `fluid ai setup`, and to `ollama` when none is saved). The forge message names two ways to author without it: `--llm-provider claude-code` (your Claude Code login) and `--template starter`; the `--agent-loop` message names only `--template starter`, because the agent CLIs lack tool use. `codex`, `cursor` and `kiro` also work without litellm, but they reuse their own CLI login or key. `--blank` and the other templates need no AI. `--llm-provider mcp-sampling` works only inside `fluid mcp serve`, which needs the `mcp` extra. Bare interactive `fluid forge` with no provider uses Claude Code when it is installed, and otherwise prints the hint and offers guided mode. +AI forge with an API-key provider needs the `litellm` extra (alias `ai`): `pip install 'data-product-forge[litellm]'`. *(since 0.28.0, [forge-cli #837](https://github.com/Agenticstiger/forge-cli/pull/837))* Without it, an explicit `--llm-provider` that names an API-key provider (`openai`, `anthropic`, `gemini`, `ollama`), on `fluid forge`, on `fluid forge data-model` or with `--agent-loop`, prints that line and exits 1 with `[ERR_COPILOT_LITELLM_UNAVAILABLE]`, before the interview reads any context or memory. `fluid forge data-model` with no `--llm-provider`, `--llm-model` or `--llm-endpoint` runs offline and needs no extra (`--llm-model` alone does not read the provider saved with `fluid ai setup`: it uses a local Ollama that answers, else `gemini`, which needs a Gemini key. *On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* With no Ollama answering, it stops with `copilot_no_llm_provider` instead.). The forge message names two ways to author without it: `--llm-provider claude-code` (your Claude Code login) and `--template starter`; the `--agent-loop` message names only `--template starter`, because the agent CLIs lack tool use. `codex`, `cursor` and `kiro` also work without litellm, but they reuse their own CLI login or key. `--blank` and the other templates need no AI. `--llm-provider mcp-sampling` works only inside `fluid mcp serve`, which needs the `mcp` extra. Bare interactive `fluid forge` with no provider uses Claude Code when it is installed, and otherwise prints the hint and offers guided mode. ## Syntax @@ -81,7 +81,7 @@ Both apply on the default AI-copilot path only. See [Single file or fragments?]( | --- | --- | | `--llm-provider NAME` | `openai`, `anthropic`, `claude`, `gemini` or `ollama`; or a keyless provider: `mcp-sampling` (the calling IDE's model) or `claude-code`, `codex`, `cursor`, `kiro` (a local agent CLI). Providers installed through the `fluid_build.llm_providers` entry point are also accepted. | | `--llm-model NAME` | Model identifier. | -| `--llm-endpoint URL` | Exact HTTP endpoint for the selected provider. The routing model's calls use it too, unless `--llm-routing-endpoint` names another. *On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* | +| `--llm-endpoint URL` | Exact HTTP endpoint for the selected provider. It does not choose the provider. *On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* The routing model's calls use it too, unless `--llm-routing-endpoint` names another. *On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* For Ollama it is read like `OLLAMA_HOST`, so `gpu-box` means `http://gpu-box:11434`, and forge's model pick, its model check and each `--tiered` stage use it too; see [One address for every Ollama read](../advanced/llm-providers.md#one-address-for-every-ollama-read). | | `--forge-agent-mode MODE` | For the local agent CLIs: `envelope` (default; the agent returns the contract as JSON on stdout) or `agentic` (the agent writes `contract.fluid.yaml` into the workspace). | | `--llm-routing-model NAME` | A fast, cheap model for interview clarification and AI self-evaluation. | | `--llm-routing-endpoint URL` | Endpoint override for the routing model. | @@ -93,7 +93,7 @@ Both apply on the default AI-copilot path only. See [Single file or fragments?]( Show the model plan per provider with [`fluid ai models`](./ai.md), and check connectivity with [`fluid ai test`](./ai.md#ai-test). -With no AI config, a run without a terminal picks a provider itself; see [When nothing is configured](../advanced/llm-providers.md#when-nothing-is-configured). *On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* +*On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* With no AI config, a run without a terminal picks a provider itself; see [When nothing is configured](../advanced/llm-providers.md#when-nothing-is-configured). *On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* When it finds none, the run stops with `copilot_no_llm_provider`. ### Prompt profiles and overlays @@ -411,7 +411,7 @@ fluid forge --agent --emit-plan - It bundles `--yes` and `FLUID_FORGE_NO_*=1`, and emits **JSON-Lines progress events** on stdout so the calling agent can stream status. - With no mode flag and no AI flag, it writes the blank contract (`--blank`), so it can never drop into the interactive mode picker. -- Any of `--llm-provider`, `--llm-model`, `--llm-endpoint` or `--context` selects AI mode instead. Forge's AI authors the contract without asking anything. A contract written under the directory you run forge in is reported in a `forge.contract_written` event; with `--target-dir` outside it, AI mode emits only `forge.start` and `forge.done`. A failed generation ends with a non-zero `exit_code` in `forge.done` and a non-zero exit status. With nothing configured, the provider is picked as in [When nothing is configured](../advanced/llm-providers.md#when-nothing-is-configured). *On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* Through 0.28.0 these flags were ignored: the run wrote the TODO placeholder contract and reported `exit_code` `0`. +- Any of `--llm-provider`, `--llm-model`, `--llm-endpoint` or `--context` selects AI mode instead. Forge's AI authors the contract without asking anything. The contract it writes is reported in a `forge.contract_written` event. A failed generation ends with a non-zero `exit_code` in `forge.done` and a non-zero exit status. With nothing configured, the provider is picked as in [When nothing is configured](../advanced/llm-providers.md#when-nothing-is-configured). *On forge-cli `main`, not yet in a release ([forge-cli #850](https://github.com/Agenticstiger/forge-cli/pull/850)).* Through 0.28.0 these flags were ignored: the run wrote the TODO placeholder contract and reported `exit_code` `0`. *On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* The event, and forge's receipt, cover a contract written to any `--target-dir`: one outside the directory you run forge in, one more than two levels below it, or one given with a literal `~`. A contract outside the current directory is reported by its absolute path. Before #852, AI mode reported only a contract within two levels of the current directory, and for another target it emitted only `forge.start` and `forge.done`. - `--emit-plan` makes the run emit a single deterministic `forge.plan` event — the per-product-type (SDP / ADP / CDP) field checklist for the agent to fill in — instead of authoring the contract itself. For example, to have a signed-in Claude Code author from an intent file: diff --git a/docs/upgrading.md b/docs/upgrading.md index d8656c0..4545fc4 100644 --- a/docs/upgrading.md +++ b/docs/upgrading.md @@ -832,6 +832,16 @@ pip install "data-product-forge[standard]" # litellm and mcp: the pre-0.28.0 AI - **Ollama calls reach the daemon.** They go to `--llm-endpoint`, else `OLLAMA_HOST`, else `localhost:11434`, and a remote `OLLAMA_HOST` is honoured. See [The Ollama endpoint](./advanced/llm-providers.md#the-ollama-endpoint). - **`fluid ai test` probes the real call.** A rejected key exits `3`, a model the provider does not have exits `4`, and other failed calls exit `5`. See [`ai test`](./cli/ai.md#ai-test). +### Follow-ups to these changes + +*On forge-cli `main`, not yet in a release ([forge-cli #852](https://github.com/Agenticstiger/forge-cli/pull/852)).* These follow-ups to the changes above can change what a script, a CI job or an IDE agent sees: + +- **No `gemini` fallback.** When nothing names a provider, forge stops with `copilot_no_llm_provider` and says why. It used to fall back to `gemini`, and sent `FLUID_LLM_API_KEY` there whatever key it was. Pass `--llm-provider`, or set `FLUID_LLM_PROVIDER` or the key of exactly one provider. See [No LLM provider is selected](./advanced/production-troubleshooting.md#no-llm-provider-is-selected). +- **`OLLAMA_HOST` may name any machine everywhere.** The probe for a running Ollama, the default-model pick and `fluid ai setup` no longer swap a non-local host for `localhost`, and `fluid ai test --endpoint` takes an Ollama address on any host. +- **Forge looks for Ollama models where it sends the call.** With `--llm-endpoint` or `FLUID_LLM_ENDPOINT`, the default-model pick and the model check ask that daemon, and each `--tiered` stage calls it. A `user:password@` in the address now goes as basic auth to the model lookups too, and `OLLAMA_HOST` no longer drops it. See [One address for every Ollama read](./advanced/llm-providers.md#one-address-for-every-ollama-read). +- **`fluid forge --agent` reports a contract written to any `--target-dir`.** See [Headless agent mode](./cli/forge.md#headless-agent-mode). +- **`LlmProvider.list_available_models` takes an `endpoint` keyword**, the address the call will go to. Forge passes it only when an endpoint is stated, so code that subclasses `LlmProvider` and overrides the method with two arguments keeps working until then. With an endpoint stated and the model check on (`--require-llm`, `--tiered` or `FLUID_LLM_MODEL_PREFLIGHT`), the check fails with `copilot_llm_model_preflight_failed`; add the keyword to avoid it. + ## DuckDB 1.x to 2.x `0.22.0` raised the local engine's DuckDB range to `duckdb>=1.5.0` with no upper bound, so