Actor options (openai_api_base, openai_api_key) ignored in Strategize/Execute paths — LLM calls fall back to real OpenAI instead of local backend #11256

Closed
opened 2026-05-28 11:40:05 +00:00 by CoreRasurae · 0 comments
Member

Metadata

Commit Message: fix(strategize): propagate actor options.openai_api_base and options.openai_api_key to LLM client creation in Strategize/Execute paths
Branch: bugfix/m5-actor-options-ignored

Background and context

When using local LLM backends (e.g., llama-cpp, LocalAI, vLLM) served via an OpenAI-compatible API endpoint, users configure actors with YAML fields under options::

options:
  openai_api_base: http://192.168.1.114:8000/v1
  openai_api_key: none

These options specify a custom API base URL and dummy API key for local backends. The intended behavior is that ChatOpenAI is initialized with these parameters, causing all LLM calls to route to the local endpoint instead of https://api.openai.com.

Current behavior

When a plan enters the Strategize phase (via agents plan useagents plan execute), the actor's options block is completely ignored. The LLM client is created with no extra kwargs, causing ChatOpenAI to fall back to OpenAI's real internet server (https://api.openai.com/v1). Since the local actor has openai_api_key: "none", the real OpenAI endpoint rejects it with 401 AuthenticationError.

Observed error:

AuthenticationError("Error code: 401 - {'error': {'message': 'Incorrect API key provided: none. You can find your API key at https://platform.openai.com/account/api-keys.', ...}}")

After 3 retries, the strategy generation falls back to a stub, and the plan fails.

The root cause is two independent gaps:

Root Cause 1: Schema silently drops options

  • ActorConfigSchema (src/cleveragents/actor/schema.py:687–808) has no options field and no model_config with extra='allow'.
  • Pydantic v2 defaults to extra='ignore', so options is silently stripped during model_validate().
  • The raw dict stored as config_blob at v3_registry.py:231 still contains options, so it is persisted — it's just never surfaced through the validated model.

Root Cause 2: Strategize/Execute LLM creation never forwards options

The following four call sites create the LLM client with zero extra kwargs:

File Line Method
src/cleveragents/application/services/strategy_actor.py 482 StrategyActor._execute_with_llm()
src/cleveragents/application/services/llm_actors.py 185 LLMStrategizeActor.execute()
src/cleveragents/application/services/llm_actors.py 421–423 LLMExecuteActor.execute()
src/cleveragents/application/services/session_workflow.py 403–407 SessionWorkflow._resolve_llm()

None of these retrieve the actor's config blob, extract options, or forward openai_api_base/openai_api_key to create_llm().

Pre-existing fix exists (but only for the reactive path)

The reactive path (actor run and streaming sessions) was already fixed via PRs #11225 and #11243. Both ToolCallingLLMCaller (src/cleveragents/reactive/tool_caller.py:167–193) and SimpleLLMAgent (src/cleveragents/reactive/stream_router.py:240–281) correctly:

  1. Extract options from the actor config
  2. Pop openai_api_key into __api_key_sentinel (bypassing the default API key resolution)
  3. Forward allowed options (openai_api_base, temperature, max_tokens, etc.) to create_llm()

The strategize/execute/session paths were never updated with the same fix, which is why this regression occurs.

Expected behavior

The Strategize and Execute phases should resolve the correct LLM endpoint from the actor's configuration. When an actor has options.openai_api_base: http://192.168.1.114:8000/v1 and options.openai_api_key: none, ChatOpenAI should be initialized with openai_api_base="http://192.168.1.114:8000/v1" and api_key="none", routing all LLM calls to the local endpoint.

The strategy actor should successfully produce a strategy, and the execution actor should execute the plan against the local LLM backend.

Acceptance criteria

  • When an actor YAML includes options.openai_api_base and options.openai_api_key, those values are forwarded to ChatOpenAI during the Strategize phase.
  • When an actor YAML includes options.openai_api_base and options.openai_api_key, those values are forwarded to ChatOpenAI during the Execute phase.
  • When an actor YAML includes options.openai_api_base and options.openai_api_key, those values are forwarded to session-based workflows (SessionWorkflow._resolve_llm()).
  • The fix reuses or extracts the existing _ALLOWED_OPTIONS and __api_key_sentinel pattern from tool_caller.py / stream_router.py, so there is a single canonical way to resolve actor options for LLM creation.
  • ActorConfigSchema is updated to either include an options field or use model_config with extra='allow' so the options are not silently dropped during validation.
  • All existing tests continue to pass (nox -s unit_tests).
  • Coverage stays at or above 97% (nox -s coverage_report).

Subtasks

  • Add options field (or extra='allow') to ActorConfigSchema in src/cleveragents/actor/schema.py.
  • Extract the shared _ALLOWED_OPTIONS / __api_key_sentinel option-forwarding logic into a reusable utility function (in actor/config.py or a new shared module).
  • Update StrategyActor._execute_with_llm() to retrieve actor options and forward them to create_llm().
  • Update LLMStrategizeActor.execute() to retrieve actor options and forward them to create_llm().
  • Update LLMExecuteActor.execute() to retrieve actor options and forward them to create_llm().
  • Update SessionWorkflow._resolve_llm() to retrieve actor options and forward them to create_llm().
  • Refactor ToolCallingLLMCaller and SimpleLLMAgent to use the shared utility instead of their inline copies.
  • Tests (Behave): Add BDD scenarios for actor option propagation in LLM creation (e.g., mock actor with options.openai_api_base set, verify ChatOpenAI receives the override).
  • Tests (Robot): Add integration test verifying that a plan with a local-backend actor resolves the correct API endpoint.
  • Verify coverage >=97% via nox -s coverage_report.
  • Run nox (all default sessions), fix any errors.

Definition of Done

This issue is complete when:

  • All subtasks above are completed and checked off.
  • A Git commit is created where the first line matches the Commit Message in Metadata exactly.
  • The commit is pushed to the branch matching the Branch in Metadata exactly.
  • The commit is submitted as a PR to master, reviewed, and merged.
## Metadata **Commit Message:** fix(strategize): propagate actor `options.openai_api_base` and `options.openai_api_key` to LLM client creation in Strategize/Execute paths **Branch:** bugfix/m5-actor-options-ignored ## Background and context When using local LLM backends (e.g., llama-cpp, LocalAI, vLLM) served via an OpenAI-compatible API endpoint, users configure actors with YAML fields under `options:`: ```yaml options: openai_api_base: http://192.168.1.114:8000/v1 openai_api_key: none ``` These options specify a custom API base URL and dummy API key for local backends. The intended behavior is that `ChatOpenAI` is initialized with these parameters, causing all LLM calls to route to the local endpoint instead of `https://api.openai.com`. ## Current behavior When a plan enters the **Strategize phase** (via `agents plan use` → `agents plan execute`), the actor's `options` block is completely ignored. The LLM client is created with **no extra kwargs**, causing `ChatOpenAI` to fall back to OpenAI's real internet server (`https://api.openai.com/v1`). Since the local actor has `openai_api_key: "none"`, the real OpenAI endpoint rejects it with `401 AuthenticationError`. **Observed error:** ``` AuthenticationError("Error code: 401 - {'error': {'message': 'Incorrect API key provided: none. You can find your API key at https://platform.openai.com/account/api-keys.', ...}}") ``` After 3 retries, the strategy generation falls back to a stub, and the plan fails. The root cause is **two independent gaps**: ### Root Cause 1: Schema silently drops `options` - `ActorConfigSchema` (`src/cleveragents/actor/schema.py:687–808`) has **no `options` field** and **no `model_config` with `extra='allow'`**. - Pydantic v2 defaults to `extra='ignore'`, so `options` is silently stripped during `model_validate()`. - The raw dict stored as `config_blob` at `v3_registry.py:231` still contains `options`, so it *is* persisted — it's just never surfaced through the validated model. ### Root Cause 2: Strategize/Execute LLM creation never forwards options The following four call sites create the LLM client with **zero extra kwargs**: | File | Line | Method | |------|------|--------| | `src/cleveragents/application/services/strategy_actor.py` | 482 | `StrategyActor._execute_with_llm()` | | `src/cleveragents/application/services/llm_actors.py` | 185 | `LLMStrategizeActor.execute()` | | `src/cleveragents/application/services/llm_actors.py` | 421–423 | `LLMExecuteActor.execute()` | | `src/cleveragents/application/services/session_workflow.py` | 403–407 | `SessionWorkflow._resolve_llm()` | None of these retrieve the actor's config blob, extract `options`, or forward `openai_api_base`/`openai_api_key` to `create_llm()`. ### Pre-existing fix exists (but only for the reactive path) The **reactive path** (`actor run` and streaming sessions) was already fixed via PRs #11225 and #11243. Both `ToolCallingLLMCaller` (`src/cleveragents/reactive/tool_caller.py:167–193`) and `SimpleLLMAgent` (`src/cleveragents/reactive/stream_router.py:240–281`) correctly: 1. Extract `options` from the actor config 2. Pop `openai_api_key` into `__api_key_sentinel` (bypassing the default API key resolution) 3. Forward allowed options (`openai_api_base`, `temperature`, `max_tokens`, etc.) to `create_llm()` The **strategize/execute/session paths were never updated** with the same fix, which is why this regression occurs. ## Expected behavior The Strategize and Execute phases should resolve the correct LLM endpoint from the actor's configuration. When an actor has `options.openai_api_base: http://192.168.1.114:8000/v1` and `options.openai_api_key: none`, `ChatOpenAI` should be initialized with `openai_api_base="http://192.168.1.114:8000/v1"` and `api_key="none"`, routing all LLM calls to the local endpoint. The strategy actor should successfully produce a strategy, and the execution actor should execute the plan against the local LLM backend. ## Acceptance criteria - [ ] When an actor YAML includes `options.openai_api_base` and `options.openai_api_key`, those values are forwarded to `ChatOpenAI` during the Strategize phase. - [ ] When an actor YAML includes `options.openai_api_base` and `options.openai_api_key`, those values are forwarded to `ChatOpenAI` during the Execute phase. - [ ] When an actor YAML includes `options.openai_api_base` and `options.openai_api_key`, those values are forwarded to session-based workflows (`SessionWorkflow._resolve_llm()`). - [ ] The fix reuses or extracts the existing `_ALLOWED_OPTIONS` and `__api_key_sentinel` pattern from `tool_caller.py` / `stream_router.py`, so there is a single canonical way to resolve actor options for LLM creation. - [ ] `ActorConfigSchema` is updated to either include an `options` field or use `model_config` with `extra='allow'` so the options are not silently dropped during validation. - [ ] All existing tests continue to pass (`nox -s unit_tests`). - [ ] Coverage stays at or above 97% (`nox -s coverage_report`). ## Subtasks - [ ] Add `options` field (or `extra='allow'`) to `ActorConfigSchema` in `src/cleveragents/actor/schema.py`. - [ ] Extract the shared `_ALLOWED_OPTIONS` / `__api_key_sentinel` option-forwarding logic into a reusable utility function (in `actor/config.py` or a new shared module). - [ ] Update `StrategyActor._execute_with_llm()` to retrieve actor options and forward them to `create_llm()`. - [ ] Update `LLMStrategizeActor.execute()` to retrieve actor options and forward them to `create_llm()`. - [ ] Update `LLMExecuteActor.execute()` to retrieve actor options and forward them to `create_llm()`. - [ ] Update `SessionWorkflow._resolve_llm()` to retrieve actor options and forward them to `create_llm()`. - [ ] Refactor `ToolCallingLLMCaller` and `SimpleLLMAgent` to use the shared utility instead of their inline copies. - [ ] Tests (Behave): Add BDD scenarios for actor option propagation in LLM creation (e.g., mock actor with `options.openai_api_base` set, verify `ChatOpenAI` receives the override). - [ ] Tests (Robot): Add integration test verifying that a plan with a local-backend actor resolves the correct API endpoint. - [ ] Verify coverage >=97% via `nox -s coverage_report`. - [ ] Run `nox` (all default sessions), fix any errors. ## Definition of Done This issue is complete when: - All subtasks above are completed and checked off. - A Git commit is created where the first line matches the Commit Message in Metadata exactly. - The commit is pushed to the branch matching the Branch in Metadata exactly. - The commit is submitted as a PR to master, reviewed, and merged.
CoreRasurae added this to the v3.4.0 milestone 2026-05-28 11:40:05 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
cleveragents/cleveragents-core#11256
No description provided.