433 lines
16 KiB
Gherkin
433 lines
16 KiB
Gherkin
Feature: Actor configuration uncovered lines coverage
|
|
As a developer maintaining actor configuration parsing
|
|
I want robust coverage for actor config edge cases
|
|
So that failures and overrides are validated
|
|
|
|
Background:
|
|
Given an isolated actor config workspace
|
|
|
|
# --- load_blob_from_file: lines 39-55 ---
|
|
|
|
Scenario: Missing config file surfaces helpful error
|
|
When I load the actor config blob from "missing.json"
|
|
Then a ValueError should be raised containing "Config file not found"
|
|
|
|
Scenario: JSON null config becomes empty mapping
|
|
Given an actor config file "null.json" with content:
|
|
"""
|
|
null
|
|
"""
|
|
When I load the actor config blob from "null.json"
|
|
Then the loaded actor config blob should equal {}
|
|
|
|
Scenario: JSON list config raises validation error
|
|
Given an actor config file "list.json" with content:
|
|
"""
|
|
[1, 2, 3]
|
|
"""
|
|
When I load the actor config blob from "list.json"
|
|
Then a ValueError should be raised containing "Config must be a JSON or YAML object"
|
|
|
|
Scenario: Valid JSON config is loaded correctly
|
|
Given an actor config file "valid.json" with content:
|
|
"""
|
|
{"provider": "openai", "model": "gpt-4"}
|
|
"""
|
|
When I load the actor config blob from "valid.json"
|
|
Then the loaded actor config blob should equal {"provider": "openai", "model": "gpt-4"}
|
|
|
|
# --- _load_v2_yaml_content: lines 59-60 (yaml unavailable) ---
|
|
|
|
Scenario: YAML dependency absence surfaces requirement
|
|
Given PyYAML parsing is unavailable for actor config
|
|
And an actor config file "needs_yaml.yaml" with content:
|
|
"""
|
|
provider: openai
|
|
"""
|
|
When I load the actor config blob from "needs_yaml.yaml"
|
|
Then a ValueError should be raised containing "PyYAML is required for YAML actor configs"
|
|
|
|
# --- _load_v2_yaml_content: lines 63-106 (YAML parsing paths) ---
|
|
|
|
Scenario: Empty YAML returns empty object
|
|
Given an actor config file "empty.yaml" with content:
|
|
"""
|
|
"""
|
|
When I load the actor config blob from "empty.yaml"
|
|
Then the loaded actor config blob should equal {}
|
|
|
|
Scenario: Non-object YAML raises validation error
|
|
Given an actor config file "list.yaml" with content:
|
|
"""
|
|
- provider: openai
|
|
"""
|
|
When I load the actor config blob from "list.yaml"
|
|
Then a ValueError should be raised containing "Config must be a JSON or YAML object"
|
|
|
|
Scenario: Plain YAML config is loaded via safe_load path
|
|
Given an actor config file "plain.yaml" with content:
|
|
"""
|
|
provider: openai
|
|
model: gpt-4
|
|
"""
|
|
When I load the actor config blob from "plain.yaml"
|
|
Then the loaded actor config blob should equal {"provider": "openai", "model": "gpt-4"}
|
|
|
|
# --- _load_v2_yaml_content: lines 64-92 (templated YAML branch) ---
|
|
|
|
Scenario: Templated YAML preserves double-brace placeholders in system_prompt
|
|
Given an actor config file "templated.yaml" with content:
|
|
"""
|
|
provider: openai
|
|
model: gpt-4
|
|
system_prompt: "Use {{ context.paper_details.topic }} to write."
|
|
"""
|
|
When I load the actor config blob from "templated.yaml"
|
|
Then the loaded actor config value at "system_prompt" should equal "Use {{ context.paper_details.topic }} to write."
|
|
|
|
Scenario: Templated YAML preserves block-tag placeholders in system_prompt
|
|
Given an actor config file "block_template.yaml" with content:
|
|
"""
|
|
provider: openai
|
|
model: gpt-4
|
|
system_prompt: "{% if context.brainstorming_summary %}summary{% endif %}"
|
|
"""
|
|
When I load the actor config blob from "block_template.yaml"
|
|
Then the loaded actor config value at "system_prompt" should contain "summary"
|
|
|
|
# --- _restore_template_syntax: lines 110-130 (dict and list recursion) ---
|
|
|
|
Scenario: Restore template syntax recurses into nested dicts
|
|
Given an actor config file "nested_template.yaml" with content:
|
|
"""
|
|
provider: openai
|
|
model: gpt-4
|
|
outer:
|
|
system_prompt: "Hello {{ context.paper_details.topic }}"
|
|
"""
|
|
When I load the actor config blob from "nested_template.yaml"
|
|
Then the loaded actor config nested value at "outer.system_prompt" should equal "Hello {{ context.paper_details.topic }}"
|
|
|
|
Scenario: Restore template syntax recurses into lists
|
|
Given an actor config file "list_template.yaml" with content:
|
|
"""
|
|
provider: openai
|
|
model: gpt-4
|
|
messages:
|
|
- role: system
|
|
system_prompt: "Topic is {{ context.paper_details.topic }}"
|
|
- role: user
|
|
content: "plain text"
|
|
"""
|
|
When I load the actor config blob from "list_template.yaml"
|
|
Then the loaded actor config nested value at "messages.0.system_prompt" should equal "Topic is {{ context.paper_details.topic }}"
|
|
|
|
# --- _interpolate_env_vars: lines 134-174 (env var substitution and type coercion) ---
|
|
|
|
Scenario: Environment variable interpolation uses default boolean value
|
|
Given an actor config file "env_bool.yaml" with content:
|
|
"""
|
|
provider: openai
|
|
model: gpt-4
|
|
options:
|
|
flag: "${MISSING_BOOL_VAR:true}"
|
|
"""
|
|
And the actor config environment variable "MISSING_BOOL_VAR" is unset
|
|
When I load the actor config blob from "env_bool.yaml"
|
|
Then the loaded actor config value at "options.flag" should equal True
|
|
|
|
Scenario: Environment variable interpolation uses default false boolean value
|
|
Given an actor config file "env_false.yaml" with content:
|
|
"""
|
|
provider: openai
|
|
model: gpt-4
|
|
options:
|
|
flag: "${MISSING_BOOL_FALSE:false}"
|
|
"""
|
|
And the actor config environment variable "MISSING_BOOL_FALSE" is unset
|
|
When I load the actor config blob from "env_false.yaml"
|
|
Then the loaded actor config value at "options.flag" should equal False
|
|
|
|
Scenario: Environment variable interpolation uses default integer value
|
|
Given an actor config file "env_int.yaml" with content:
|
|
"""
|
|
provider: openai
|
|
model: gpt-4
|
|
options:
|
|
count: "${MISSING_INT_VAR:42}"
|
|
"""
|
|
And the actor config environment variable "MISSING_INT_VAR" is unset
|
|
When I load the actor config blob from "env_int.yaml"
|
|
Then the loaded actor config value at "options.count" should equal 42
|
|
|
|
Scenario: Environment variable interpolation uses default string value
|
|
Given an actor config file "env_str.yaml" with content:
|
|
"""
|
|
provider: openai
|
|
model: gpt-4
|
|
options:
|
|
greeting: "${MISSING_STR_VAR:hello}"
|
|
"""
|
|
And the actor config environment variable "MISSING_STR_VAR" is unset
|
|
When I load the actor config blob from "env_str.yaml"
|
|
Then the loaded actor config value at "options.greeting" should equal "hello"
|
|
|
|
Scenario: Environment variable interpolation uses default float value
|
|
Given an actor config file "env_float.yaml" with content:
|
|
"""
|
|
provider: openai
|
|
model: gpt-4
|
|
options:
|
|
ratio: "${MISSING_FLOAT_VAR:2.5}"
|
|
"""
|
|
And the actor config environment variable "MISSING_FLOAT_VAR" is unset
|
|
When I load the actor config blob from "env_float.yaml"
|
|
Then the loaded actor config value at "options.ratio" should equal 2.5
|
|
|
|
Scenario: Environment variable interpolation reads set env var
|
|
Given an actor config file "env_set.yaml" with content:
|
|
"""
|
|
provider: openai
|
|
model: gpt-4
|
|
options:
|
|
api_key: "${TEST_ACTOR_API_KEY}"
|
|
"""
|
|
And the actor config environment variable "TEST_ACTOR_API_KEY" is set to "secret123"
|
|
When I load the actor config blob from "env_set.yaml"
|
|
Then the loaded actor config value at "options.api_key" should equal "secret123"
|
|
|
|
Scenario: Missing required environment variable raises an error
|
|
Given an actor config file "required_env.yaml" with content:
|
|
"""
|
|
provider: openai
|
|
model: gpt-4
|
|
secret: "${REQUIRED_SECRET}"
|
|
"""
|
|
And the actor config environment variable "REQUIRED_SECRET" is unset
|
|
When I load the actor config blob from "required_env.yaml"
|
|
Then a ValueError should be raised containing "Environment variable 'REQUIRED_SECRET' is not set"
|
|
|
|
Scenario: Environment variable coerces top-level string true to boolean
|
|
Given an actor config file "env_top_bool.yaml" with content:
|
|
"""
|
|
provider: openai
|
|
model: gpt-4
|
|
enabled: true
|
|
"""
|
|
When I load the actor config blob from "env_top_bool.yaml"
|
|
Then the loaded actor config value at "enabled" should equal True
|
|
|
|
Scenario: Environment variable coerces string integer to int
|
|
Given an actor config file "env_top_int.yaml" with content:
|
|
"""
|
|
provider: openai
|
|
model: gpt-4
|
|
retries: 3
|
|
"""
|
|
When I load the actor config blob from "env_top_int.yaml"
|
|
Then the loaded actor config value at "retries" should equal 3
|
|
|
|
Scenario: Interpolation coerces negative integer default
|
|
Given an actor config file "env_neg_int.yaml" with content:
|
|
"""
|
|
provider: openai
|
|
model: gpt-4
|
|
options:
|
|
offset: "${MISSING_NEG_INT:-5}"
|
|
"""
|
|
And the actor config environment variable "MISSING_NEG_INT" is unset
|
|
When I load the actor config blob from "env_neg_int.yaml"
|
|
Then the loaded actor config value at "options.offset" should equal -5
|
|
|
|
Scenario: Interpolation recurses into nested dicts and lists
|
|
Given an actor config file "env_nested.yaml" with content:
|
|
"""
|
|
provider: openai
|
|
model: gpt-4
|
|
options:
|
|
items:
|
|
- "${MISSING_ITEM_VAR:default_item}"
|
|
"""
|
|
And the actor config environment variable "MISSING_ITEM_VAR" is unset
|
|
When I load the actor config blob from "env_nested.yaml"
|
|
Then the loaded actor config nested value at "options.items.0" should equal "default_item"
|
|
|
|
# --- from_file: lines 189-190 ---
|
|
|
|
Scenario: from_file applies overrides and unsafe flag
|
|
Given an actor config file "valid_ff.json" with content:
|
|
"""
|
|
{"provider": "file-provider", "model": "file-model", "options": {"k": "v"}, "graph_descriptor": {"node": true}}
|
|
"""
|
|
When I parse the actor configuration from file "valid_ff.json" with overrides:
|
|
"""
|
|
{"name": "cli-name", "graph_descriptor": {"node": "override"}, "unsafe": true}
|
|
"""
|
|
Then the actor configuration should have provider "file-provider" and model "file-model"
|
|
And the actor configuration name should be "cli-name"
|
|
And the actor configuration graph descriptor should equal {"node": "override"}
|
|
And the actor configuration options should equal {"k": "v"}
|
|
And the actor configuration unsafe flag should be true
|
|
|
|
# --- from_blob: lines 236 (default_options), 238 (v2_options), 250 (missing model) ---
|
|
|
|
Scenario: from_blob rejects missing provider
|
|
When I build an actor configuration from blob {"model": "only-model"}
|
|
Then a ValueError should be raised containing "provider is required"
|
|
|
|
Scenario: from_blob rejects missing model
|
|
When I build an actor configuration from blob {"provider": "only-provider"}
|
|
Then a ValueError should be raised containing "model is required"
|
|
|
|
Scenario: from_blob merges default and v2 and override options
|
|
When I build an actor configuration from structured blob with defaults and overrides:
|
|
"""
|
|
{
|
|
"blob": {
|
|
"provider": "cli-provider",
|
|
"model": "cli-model",
|
|
"options": {"user": "blob"},
|
|
"agents": {
|
|
"writer": {
|
|
"config": {
|
|
"options": {"v2": "v2-option"}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"default_options": {"base": "default"},
|
|
"option_overrides": {"user": "override", "extra": "added"}
|
|
}
|
|
"""
|
|
Then the actor configuration should have provider "cli-provider" and model "cli-model"
|
|
And the actor configuration options should equal {"base": "default", "v2": "v2-option", "user": "override", "extra": "added"}
|
|
|
|
Scenario: from_blob with None blob defaults to empty data
|
|
When I build actor config from None blob with provider and model overrides
|
|
Then the actor configuration should have provider "override-provider" and model "override-model"
|
|
|
|
# --- _extract_v2_actor: lines 275-307 ---
|
|
|
|
Scenario: v2 YAML actor config infers provider and model
|
|
Given an actor config file "v2.yaml" with content:
|
|
"""
|
|
cleveragents:
|
|
default_router: main_router
|
|
agents:
|
|
paper_writer:
|
|
type: llm
|
|
config:
|
|
provider: openai
|
|
model: gpt-4
|
|
unsafe: true
|
|
options:
|
|
temperature: 0.5
|
|
routes:
|
|
main_router:
|
|
type: stream
|
|
operators:
|
|
- type: map
|
|
params:
|
|
agent: paper_writer
|
|
publications:
|
|
- __output__
|
|
"""
|
|
When I parse the actor configuration from file "v2.yaml" with overrides:
|
|
"""
|
|
{}
|
|
"""
|
|
Then the actor configuration should have provider "openai" and model "gpt-4"
|
|
And the actor configuration unsafe flag should be true
|
|
And the actor configuration graph descriptor should include key "routes"
|
|
And the actor configuration graph descriptor should include key "agents"
|
|
And the actor configuration graph descriptor should include key "cleveragents"
|
|
And the actor configuration options should equal {"temperature": 0.5}
|
|
|
|
Scenario: v2 extraction includes merges and templates keys when present
|
|
Given an actor config file "v2_merges.yaml" with content:
|
|
"""
|
|
agents:
|
|
writer:
|
|
config:
|
|
provider: openai
|
|
model: gpt-4
|
|
merges:
|
|
combined:
|
|
type: join
|
|
templates:
|
|
default:
|
|
system_prompt: "hello"
|
|
"""
|
|
When I parse the actor configuration from file "v2_merges.yaml" with overrides:
|
|
"""
|
|
{}
|
|
"""
|
|
Then the actor configuration should have provider "openai" and model "gpt-4"
|
|
And the actor configuration graph descriptor should include key "merges"
|
|
And the actor configuration graph descriptor should include key "templates"
|
|
|
|
Scenario: v2 extraction handles agent entry without config block
|
|
Given an actor config file "v2_no_config.yaml" with content:
|
|
"""
|
|
provider: fallback-provider
|
|
model: fallback-model
|
|
agents:
|
|
first:
|
|
type: llm
|
|
"""
|
|
When I parse the actor configuration from file "v2_no_config.yaml" with overrides:
|
|
"""
|
|
{}
|
|
"""
|
|
Then the actor configuration should have provider "fallback-provider" and model "fallback-model"
|
|
|
|
# --- _extract_v2_options: lines 316-326 ---
|
|
|
|
Scenario: v2 extraction skips non-dict agent entries
|
|
Given an actor config file "invalid_v2.yaml" with content:
|
|
"""
|
|
provider: fallback-provider
|
|
model: fallback-model
|
|
agents:
|
|
first: not-a-dict
|
|
"""
|
|
When I parse the actor configuration from file "invalid_v2.yaml" with overrides:
|
|
"""
|
|
{}
|
|
"""
|
|
Then the actor configuration should have provider "fallback-provider" and model "fallback-model"
|
|
|
|
Scenario: v2 options extraction returns None for agents without options
|
|
Given an actor config file "v2_no_opts.yaml" with content:
|
|
"""
|
|
agents:
|
|
writer:
|
|
config:
|
|
provider: openai
|
|
model: gpt-4
|
|
"""
|
|
When I parse the actor configuration from file "v2_no_opts.yaml" with overrides:
|
|
"""
|
|
{}
|
|
"""
|
|
Then the actor configuration should have provider "openai" and model "gpt-4"
|
|
And the actor configuration options should equal {}
|
|
|
|
Scenario: from_blob reads provider_type and model_id aliases
|
|
When I build an actor configuration from blob {"provider_type": "aliased-provider", "model_id": "aliased-model"}
|
|
Then the actor configuration should have provider "aliased-provider" and model "aliased-model"
|
|
|
|
Scenario: from_blob resolves graph from graph key alias
|
|
When I build actor config from blob using graph key alias
|
|
Then the actor configuration graph descriptor should equal {"step": "one"}
|
|
|
|
Scenario: from_blob sets unsafe from blob data
|
|
When I build an actor configuration from blob {"provider": "p", "model": "m", "unsafe": True}
|
|
Then the actor configuration should have provider "p" and model "m"
|
|
And the actor configuration unsafe flag should be true
|
|
|
|
Scenario: from_blob with non-dict graph coerces to None
|
|
When I build an actor configuration from blob {"provider": "p", "model": "m", "graph_descriptor": "not-a-dict"}
|
|
Then the actor configuration should have provider "p" and model "m"
|
|
And the actor configuration graph descriptor should be None
|