mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-26 07:30:11 +00:00
* feat: port NPA psychographic profiling system into IronClaw
Port the complete psychographic profiling system from NPA into IronClaw,
including enriched profile schema, conversational onboarding, profile
evolution, and three-tier prompt augmentation.
Personal onboarding moved from wizard Step 9 to first assistant
interaction per maintainer feedback — the First Contact system prompt
block now instructs the LLM to conduct a natural onboarding conversation
that builds the psychographic profile via memory_write.
Changes:
- Enrich profile.rs with 5 new structs, 9-dimension analysis framework,
custom deserializers for backward compatibility, and rendering methods
- Add conversational onboarding engine with one-step-removed questioning
technique, personality framework, and confidence-scored profile generation
- Add profile evolution with confidence gating, analysis metadata tracking,
and weekly update routine
- Replace thin interaction style injection with three-tier system gated on
confidence > 0.6 and profile recency
- Replace wizard Step 9 with First Contact system prompt block that drives
conversational onboarding during the user's first interaction
- Add autonomy progression to SOUL.md seed and personality framework to
AGENTS.md seed
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* feat: replace chat-based onboarding with bootstrap greeting and workspace seeds
Remove the interactive onboarding_chat.rs engine in favor of a simpler
bootstrap flow: fresh workspaces get a proactive LLM greeting that
naturally profiles the user. Identity files are now seeded from
src/workspace/seeds/ instead of being hardcoded. Also removes the
identity-file write protection (seeds are now managed), adds routine
advisor integration, and includes an e2e trace for bootstrap greeting.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* feat(safety): sanitize identity file writes via Sanitizer to prevent prompt injection
Identity files (SOUL.md, AGENTS.md, USER.md, IDENTITY.md) are injected into
every system prompt. Rather than hard-blocking writes (which broke onboarding),
scan content through the existing Sanitizer and reject writes with High/Critical
severity injection patterns. Medium/Low warnings are logged but allowed.
Also clarifies AGENTS.md identity file roles (USER.md = user info, IDENTITY.md =
agent identity) and adds IDENTITY.md setup as an explicit bootstrap step.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* docs: update profile_onboarding_completed comment to reflect current wiring
The field is now actively used by the agent loop to suppress BOOTSTRAP.md
injection — remove the stale "not yet wired" TODO.
[skip-regression-check]
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix(setup): use env_or_override for NEARAI_API_KEY in model fetch config
When the user authenticates via NEAR AI Cloud API key (option 4),
api_key_login() stores the key via set_runtime_env(). But
build_nearai_model_fetch_config() was using std::env::var() which
doesn't check the runtime overlay — so model listing fell back to
session-token auth and re-triggered the interactive NEAR AI
authentication menu.
Switch to env_or_override() which checks both real env vars and the
runtime overlay.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix(agent): correct channel/user_id in bootstrap greeting persist call
persist_assistant_response was called with channel="default",
user_id="system" but the assistant thread was created via
get_or_create_assistant_conversation("default", "gateway") which owns
the conversation as user_id="default", channel="gateway". The mismatch
caused ensure_writable_conversation to reject the write with:
WARN Rejected write for unavailable thread id user=system channel=default
[skip-regression-check]
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix(web): remove all inline event handlers for CSP compliance
The Content-Security-Policy header (added in f48fe95) blocks inline JS
via script-src 'self'. All onclick/onchange attributes in index.html
are replaced with getElementById().addEventListener() calls. Dynamic
inline handlers in app.js (jobs, routines, memory breadcrumb, code
blocks, TEE report) are replaced with data-action attributes and a
single delegated click handler on document.
[skip-regression-check]
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix(agent): align bootstrap message user/channel and update fixture schema field
- Bootstrap IncomingMessage now uses ("default", "gateway") consistently
with persist and session registration calls
- Update bootstrap_greeting.json fixture: schema_version → version to
match current PROFILE_JSON_SCHEMA
[skip-regression-check]
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* style: cargo fmt
[skip-regression-check]
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix(safety): address PR review — expand injection scanning and harden profile sync
- BOOTSTRAP.md: fix target "profile" → "context/profile.json" so the
write hits the correct path and triggers profile sync
- IDENTITY_FILES: add context/assistant-directives.md to the scanned
set since it is also injected into the system prompt
- sync_profile_documents(): scan derived USER.md and assistant-directives
content through Sanitizer before writing, rejecting High/Critical
injection patterns
- profile_evolution_prompt(): wrap recent_messages_summary in <user_data>
delimiters with untrusted-data instruction to mitigate indirect
prompt injection
- routine-advisor skill: update cron examples from 6-field to standard
5-field format for consistency with routine_create tool docs
[skip-regression-check]
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* style: cargo fmt
[skip-regression-check]
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix(setup): detect env-provided LLM keys during quick-mode onboarding
Quick-mode wizard now checks LLM_BACKEND, NEARAI_API_KEY,
ANTHROPIC_API_KEY, and OPENAI_API_KEY env vars to pre-populate
the provider setting, so users aren't re-prompted for credentials
they already supplied. Also teaches setup_nearai() to recognize
NEARAI_API_KEY from env (previously only checked session tokens).
Includes web UI cleanup (remove duplicate event listeners) and
e2e test response count adjustment.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix(test): update routine_create_list to expect 7-field normalized cron
The cron normalizer now always expands to 7-field format, so the
stored schedule is "0 0 9 * * * *" not "0 0 9 * * *".
[skip-regression-check]
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* feat(setup): skip LLM provider prompts when NEARAI_API_KEY is present
In quick mode, if NEARAI_API_KEY is set in the environment and the
backend was auto-detected as nearai, skip the interactive inference
provider and model selection steps. The API key is persisted to the
secrets store and a default model is set automatically.
Also simplify the static fallback model list for nearai to a single
default entry.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix: unify default model, static bootstrap greeting, and web UI cleanup
- Add DEFAULT_MODEL const and default_models() fallback list in
llm/nearai_chat.rs; use from config, wizard, and .env.example so the
default model is defined in one place
- Restore multi-model fallback list in setup wizard (was reduced to 1)
- Move BOOTSTRAP_GREETING to module-level const (out of run() body)
- Replace LLM-based bootstrap with static greeting (persist to DB before
channels start, then broadcast — eliminates startup LLM call and race)
- Fix double env::var read for NEARAI_API_KEY in quick setup path
- Move thread sidebar buttons into threads-section-header (web UI)
- Remove orphaned .thread-sidebar-header CSS and fix double blank line
- Update bootstrap e2e test for static greeting (no LLM trace needed)
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix(safety): move prompt injection scanning into Workspace write/append
Addresses PR #927 review comments (#1, #3) — identity file write
protection and unsanitized profile fields in system prompt.
Instead of scanning at the tool layer (memory.rs) or the sync layer
(sync_profile_documents), injection scanning now lives in
Workspace::write() and Workspace::append() for all files that are
injected into the system prompt. This ensures every code path that
writes to these files is protected, including future ones.
- Add SYSTEM_PROMPT_FILES const and reject_if_injected() in workspace
- Add WorkspaceError::InjectionRejected variant
- Add map_write_err() in memory.rs to convert InjectionRejected to
ToolError::NotAuthorized
- Remove redundant IDENTITY_FILES/Sanitizer from memory.rs
- Remove redundant sanitizer calls from sync_profile_documents()
- Move sanitization tests to workspace::tests
- Existing integration test (test_memory_write_rejects_injection)
continues to pass through the new path
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* style: cargo fmt
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix: address Copilot review — merge marker order, orphan thread, stale fixture
- merge_profile_section: search for END marker after BEGIN position to
avoid matching a stray END earlier in the file
- Bootstrap phase 2: use get_or_create_session + Thread::with_id instead
of resolve_thread(None) to avoid creating an orphan thread
- setup_nearai: use env_or_override for NEARAI_API_KEY consistency with
runtime overlay
- Delete orphaned bootstrap_greeting.json fixture (no test references it)
- Add test_merge_end_marker_must_follow_begin regression test
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* style: cargo fmt
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* style: fmt agent_loop.rs (CI stable rustfmt)
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix: lazy-init sanitizer, check profile non-empty before skipping bootstrap
Address Copilot review:
- Use LazyLock<Sanitizer> to avoid rebuilding Aho-Corasick + regexes
on every workspace write
- has_profile check now requires non-empty content, not just file
existence, to prevent empty profile.json from suppressing onboarding
- Add seed_tests integration tests (libsql-backed) verifying:
- Empty profile.json does not suppress BOOTSTRAP.md seeding
- Non-empty profile.json correctly suppresses bootstrap for upgrades
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* style: cargo fmt
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix: duplicate language handler, empty LLM_BACKEND, test_rig style
Address Copilot review on PR #927:
- Remove duplicate language-option click listeners (delegated
data-action handler already covers them)
- Guard LLM_BACKEND env prefill against empty string to prevent
suppressing API-key-based auto-detection
- Use destructured local `keep_bootstrap` instead of `self.keep_bootstrap`
in test_rig for consistency after destructure
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix: update stale BOOTSTRAP.md write-protection comment [skip-regression-check]
BOOTSTRAP.md is now in SYSTEM_PROMPT_FILES and gets injection scanning
on write. The old comment incorrectly stated it was not write-protected.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix: replace debug_assert panics with graceful error returns [skip-regression-check]
debug_assert! in execute_tool_with_safety and JobContext::transition_to
panicked in test builds before the graceful error path could run.
Existing tests (test_cancel_job_completed, test_execute_empty_tool_name_returns_not_found)
already cover these paths — they were the ones failing.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix: address Copilot review — schema label, env var check, path normalization, profile validation
1. Label ANALYSIS_FRAMEWORK and PROFILE_JSON_SCHEMA sections separately
in bootstrap prompt so the LLM knows which blob is the target structure.
2. Wizard quick-mode backend auto-detection now rejects empty env vars
(std::env::var().is_ok_and(|v| !v.is_empty())) to avoid selecting the
wrong backend when e.g. NEARAI_API_KEY="" is set.
3. Normalize the target path before comparing with paths::PROFILE in
memory_write so non-canonical variants like "context//profile.json"
still trigger profile sync.
4. seed_if_empty now requires valid JSON parse of context/profile.json
before treating it as a populated profile. Corrupted content no longer
permanently suppresses bootstrap seeding.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* style: cargo fmt
* fix: address Copilot review — append scan, profile validation, env_or_override
1. Workspace::append() now scans the combined content (existing + new)
for prompt injection, not just the appended chunk. Prevents split-
injection evasion across multiple appends.
2. seed_if_empty() now deserializes into PsychographicProfile instead of
serde_json::Value for profile validation. Stray/legacy JSON that
doesn't match the expected schema no longer suppresses bootstrap.
3. Wizard quick-mode backend auto-detection now uses env_or_override()
to honor runtime overlays and injected secrets. LLM_BACKEND value
is trimmed before storage.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* test: add bootstrap_onboarding_clears_bootstrap E2E trace test
Exercises the full onboarding flow end-to-end:
1. Bootstrap greeting fires automatically on fresh workspace
2. User converses for 3 turns (name, tools, work style)
3. Agent writes psychographic profile to context/profile.json
4. Profile sync generates USER.md and assistant-directives.md
5. Agent writes IDENTITY.md (chosen persona)
6. Agent clears BOOTSTRAP.md via memory_write(target: "bootstrap")
Verifies:
- BOOTSTRAP.md is non-empty before onboarding, empty after
- bootstrap_completed flag is set
- Profile contains expected user data (name, profession, interests)
- USER.md contains profile-derived content (name, tone, profession)
- Assistant-directives.md references user and communication style
- IDENTITY.md contains agent's chosen persona name
- All memory_write calls succeed
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix: address Copilot review — slash collapse, env_or_override, cron trim [skip-regression-check]
1. memory.rs path normalization now uses the same char-by-char loop as
Workspace::normalize_path() to fully collapse consecutive slashes
(e.g. "context///profile.json" → "context/profile.json").
2. Quick-mode NEARAI_API_KEY check (line 239) now uses env_or_override()
consistently with the backend auto-detection block above it.
3. normalize_cron_expression() trims input before field counting so the
passthrough branch (7+ fields) also strips whitespace.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---------
Co-authored-by: Jay Zalowitz <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
237 lines
9.4 KiB
Rust
237 lines
9.4 KiB
Rust
//! LLM configuration types.
|
||
//!
|
||
//! These types define the configuration for LLM providers. They are defined
|
||
//! here (in the `llm` module) so that the module is self-contained and can be
|
||
//! extracted into a standalone crate. Resolution logic (reading env vars,
|
||
//! settings) lives in `crate::config::llm`.
|
||
|
||
use std::path::PathBuf;
|
||
|
||
use secrecy::SecretString;
|
||
|
||
use crate::llm::registry::ProviderProtocol;
|
||
use crate::llm::session::SessionConfig;
|
||
|
||
/// Sentinel value used as `api_key` when only an OAuth token is present.
|
||
///
|
||
/// When we only have an OAuth token the provider factory in `llm/mod.rs`
|
||
/// checks for this value and routes to `AnthropicOAuthProvider`, so this
|
||
/// placeholder is never sent over the wire.
|
||
pub const OAUTH_PLACEHOLDER: &str = "oauth-placeholder";
|
||
|
||
/// Prompt cache retention policy for Anthropic.
|
||
///
|
||
/// Controls Anthropic's automatic prompt caching via a top-level
|
||
/// `cache_control` field injected through rig-core's `additional_params`.
|
||
/// - `None` — caching disabled, no `cache_control` injected.
|
||
/// - `Short` — 5-minute TTL (default), `{"type": "ephemeral"}`, 1.25× write surcharge.
|
||
/// - `Long` — 1-hour TTL, `{"type": "ephemeral", "ttl": "1h"}`, 2× write surcharge.
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||
pub enum CacheRetention {
|
||
/// No prompt caching.
|
||
None,
|
||
/// 5-minute TTL (default). Write cost: 1.25× base input.
|
||
#[default]
|
||
Short,
|
||
/// 1-hour TTL. Write cost: 2× base input.
|
||
Long,
|
||
}
|
||
|
||
impl std::str::FromStr for CacheRetention {
|
||
type Err = String;
|
||
|
||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||
match s.to_lowercase().as_str() {
|
||
"none" | "off" | "disabled" => Ok(Self::None),
|
||
"short" | "5m" | "ephemeral" => Ok(Self::Short),
|
||
"long" | "1h" => Ok(Self::Long),
|
||
_ => Err(format!(
|
||
"invalid cache retention '{}', expected one of: none, short, long",
|
||
s
|
||
)),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl std::fmt::Display for CacheRetention {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
match self {
|
||
Self::None => write!(f, "none"),
|
||
Self::Short => write!(f, "short"),
|
||
Self::Long => write!(f, "long"),
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Resolved configuration for a registry-based provider.
|
||
///
|
||
/// This single struct replaces what used to be five separate config types
|
||
/// (`OpenAiDirectConfig`, `AnthropicDirectConfig`, `OllamaConfig`,
|
||
/// `OpenAiCompatibleConfig`, `TinfoilConfig`). The `protocol` field
|
||
/// determines which rig-core client constructor to use.
|
||
#[derive(Debug, Clone)]
|
||
pub struct RegistryProviderConfig {
|
||
/// Which API protocol to use (determines the rig-core client).
|
||
pub protocol: ProviderProtocol,
|
||
/// Provider identifier (e.g., "groq", "openai", "tinfoil").
|
||
pub provider_id: String,
|
||
/// API key (optional for some providers like Ollama).
|
||
/// For Anthropic OAuth, this is set to `OAUTH_PLACEHOLDER`.
|
||
pub api_key: Option<SecretString>,
|
||
/// Base URL for the API endpoint.
|
||
pub base_url: String,
|
||
/// Model identifier.
|
||
pub model: String,
|
||
/// Extra HTTP headers injected into every request.
|
||
pub extra_headers: Vec<(String, String)>,
|
||
/// OAuth token for providers that support Bearer auth (e.g. Anthropic via `claude login`).
|
||
/// When set, the provider factory routes to the OAuth-specific provider implementation.
|
||
pub oauth_token: Option<SecretString>,
|
||
/// When true, route OpenAI-compatible traffic to the Codex ChatGPT
|
||
/// Responses API provider instead of rig-core's Chat Completions path.
|
||
pub is_codex_chatgpt: bool,
|
||
/// OAuth refresh token for Codex ChatGPT token refresh.
|
||
pub refresh_token: Option<SecretString>,
|
||
/// Path to Codex auth.json for persisting refreshed tokens.
|
||
pub auth_path: Option<PathBuf>,
|
||
/// Prompt cache retention (Anthropic-specific).
|
||
pub cache_retention: CacheRetention,
|
||
/// Parameter names that this provider does not support (e.g., `["temperature"]`).
|
||
/// Supported keys: `"temperature"`, `"max_tokens"`, `"stop_sequences"`.
|
||
/// Listed parameters are stripped from requests before sending to avoid 400 errors.
|
||
pub unsupported_params: Vec<String>,
|
||
}
|
||
|
||
/// Configuration for AWS Bedrock (native Converse API).
|
||
#[derive(Debug, Clone)]
|
||
pub struct BedrockConfig {
|
||
/// AWS region (e.g. "us-east-1").
|
||
pub region: String,
|
||
/// Bedrock model ID (e.g. "anthropic.claude-opus-4-6-v1").
|
||
pub model: String,
|
||
/// Cross-region inference prefix: "us", "eu", "apac", "global", or None.
|
||
pub cross_region: Option<String>,
|
||
/// AWS named profile (for SSO / assume-role workflows).
|
||
pub profile: Option<String>,
|
||
}
|
||
|
||
/// LLM provider configuration.
|
||
///
|
||
/// NearAI remains the default backend with its own config struct (session auth).
|
||
/// All other providers are resolved through the provider registry, producing
|
||
/// a generic `RegistryProviderConfig`.
|
||
#[derive(Debug, Clone)]
|
||
pub struct LlmConfig {
|
||
/// Backend identifier (e.g., "nearai", "openai", "groq", "tinfoil").
|
||
pub backend: String,
|
||
/// Session manager configuration (auth URL, token persistence path).
|
||
/// Used by the NearAI provider for OAuth/session-token auth.
|
||
pub session: SessionConfig,
|
||
/// NEAR AI config (always populated, also used for embeddings).
|
||
pub nearai: NearAiConfig,
|
||
/// Resolved provider config for registry-based providers.
|
||
/// `None` when backend is "nearai" or "bedrock".
|
||
pub provider: Option<RegistryProviderConfig>,
|
||
/// AWS Bedrock config (populated when backend=bedrock, requires --features bedrock).
|
||
pub bedrock: Option<BedrockConfig>,
|
||
/// HTTP request timeout in seconds for LLM API calls.
|
||
/// Default: 120. Increase for local LLMs (Ollama, vLLM, LM Studio) that
|
||
/// need more time for prompt evaluation on consumer hardware.
|
||
pub request_timeout_secs: u64,
|
||
/// Generic cheap/fast model for lightweight tasks (heartbeat, routing, evaluation).
|
||
/// Works with any backend. Set via `LLM_CHEAP_MODEL` env var.
|
||
/// When set, takes priority over the NearAI-specific `NEARAI_CHEAP_MODEL`.
|
||
pub cheap_model: Option<String>,
|
||
/// Enable cascade mode for smart routing (retry with primary if cheap model
|
||
/// response seems uncertain). Default: true. Set via `SMART_ROUTING_CASCADE`.
|
||
pub smart_routing_cascade: bool,
|
||
}
|
||
|
||
impl LlmConfig {
|
||
/// Resolve the effective cheap model name.
|
||
///
|
||
/// Resolution order:
|
||
/// 1. `LLM_CHEAP_MODEL` (generic, works with any backend)
|
||
/// 2. `NEARAI_CHEAP_MODEL` (NearAI-only, backward compatibility)
|
||
pub fn cheap_model_name(&self) -> Option<&str> {
|
||
self.cheap_model.as_deref().or_else(|| {
|
||
if self.backend == "nearai" {
|
||
self.nearai.cheap_model.as_deref()
|
||
} else {
|
||
None
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
/// NEAR AI configuration.
|
||
#[derive(Debug, Clone)]
|
||
pub struct NearAiConfig {
|
||
/// Model to use (e.g., "claude-3-5-sonnet-20241022", "gpt-4o")
|
||
pub model: String,
|
||
/// Cheap/fast model for lightweight tasks (heartbeat, routing, evaluation).
|
||
pub cheap_model: Option<String>,
|
||
/// Base URL for the NEAR AI API.
|
||
pub base_url: String,
|
||
/// API key for NEAR AI Cloud.
|
||
pub api_key: Option<SecretString>,
|
||
/// Optional fallback model for failover.
|
||
pub fallback_model: Option<String>,
|
||
/// Maximum number of retries for transient errors (default: 3).
|
||
pub max_retries: u32,
|
||
/// Consecutive failures before circuit breaker opens. None = disabled.
|
||
pub circuit_breaker_threshold: Option<u32>,
|
||
/// Seconds the circuit stays open before probing (default: 30).
|
||
pub circuit_breaker_recovery_secs: u64,
|
||
/// Enable in-memory response caching. Default: false.
|
||
pub response_cache_enabled: bool,
|
||
/// TTL in seconds for cached responses (default: 3600).
|
||
pub response_cache_ttl_secs: u64,
|
||
/// Max cached responses before LRU eviction (default: 1000).
|
||
pub response_cache_max_entries: usize,
|
||
/// Cooldown duration in seconds for failover (default: 300).
|
||
pub failover_cooldown_secs: u64,
|
||
/// Consecutive failures before failover cooldown (default: 3).
|
||
pub failover_cooldown_threshold: u32,
|
||
/// Enable cascade mode for smart routing. Default: true.
|
||
pub smart_routing_cascade: bool,
|
||
}
|
||
|
||
impl NearAiConfig {
|
||
/// Create a minimal config suitable for listing available models.
|
||
///
|
||
/// Reads `NEARAI_API_KEY` from the environment and selects the
|
||
/// appropriate base URL (cloud-api when API key is present,
|
||
/// private.near.ai for session-token auth).
|
||
pub(crate) fn for_model_discovery() -> Self {
|
||
let api_key = crate::config::helpers::env_or_override("NEARAI_API_KEY")
|
||
.filter(|k| !k.is_empty())
|
||
.map(SecretString::from);
|
||
|
||
let default_base = if api_key.is_some() {
|
||
"https://cloud-api.near.ai"
|
||
} else {
|
||
"https://private.near.ai"
|
||
};
|
||
let base_url =
|
||
std::env::var("NEARAI_BASE_URL").unwrap_or_else(|_| default_base.to_string());
|
||
|
||
Self {
|
||
model: String::new(),
|
||
cheap_model: None,
|
||
base_url,
|
||
api_key,
|
||
fallback_model: None,
|
||
max_retries: 3,
|
||
circuit_breaker_threshold: None,
|
||
circuit_breaker_recovery_secs: 30,
|
||
response_cache_enabled: false,
|
||
response_cache_ttl_secs: 3600,
|
||
response_cache_max_entries: 1000,
|
||
failover_cooldown_secs: 300,
|
||
failover_cooldown_threshold: 3,
|
||
smart_routing_cascade: true,
|
||
}
|
||
}
|
||
}
|