Skip to content

Latest commit

 

History

History
506 lines (363 loc) · 17.6 KB

File metadata and controls

506 lines (363 loc) · 17.6 KB

Configuration

Language: English · Tiếng Việt · 中文

Every knob, every env var, every precedence rule. If you're deploying OpenSpace × OpenViking, read this page end to end.


Quick start

Simplest working configuration — single-machine dev, local Viking, single user:

# OpenViking runs with defaults; just make sure it's reachable
docker run -d -p 1933:1933 volcengine/openviking:latest

# In your OpenSpace shell / .env
export OPENVIKING_ENABLED=true
# Everything else defaults correctly; $USER provides per-user isolation

openspace --query "analyze my sales spreadsheet"

That's it. The first run has no memory → same cost as before. The second similar run retrieves what was learned.


Environment variables

Variable Default Purpose
OPENVIKING_ENABLED (auto-detect) Master switch. Set to false to fully disable integration.
OPENVIKING_URL http://127.0.0.1:1933 Viking server base URL
OPENVIKING_API_KEY (empty) Optional API key sent as X-API-Key header
OPENVIKING_NAMESPACE (empty) Team/tenant prefix for URIs. Never auto-derived.
OPENVIKING_USER_ID (fallback chain) Per-user prefix for user memories
OPENVIKING_PUSH_SKILLS (config default) Override for structured skill resource push
OPENVIKING_MIN_SCORE 0.0 Drop retrievals below this score (0.0–1.0)
OPENVIKING_SCRUB_PII true Scrub secrets/PII before writing to Viking

OPENVIKING_ENABLED

Values: true / false (case-insensitive). Anything else → treated as enabled if config allows.

Precedence:

  1. Explicit env var value (if parseable as true/false)
  2. OpenSpaceConfig.openviking_enabled (defaults to True)

When to set false: benchmark runs, debugging, privacy-sensitive one-off tasks, when you want zero Viking HTTP calls.

OPENVIKING_URL

Values: Any valid URL. Trailing slash is stripped.

Examples:

  • http://127.0.0.1:1933 (local dev, default)
  • http://viking.internal:1933 (team LAN)
  • https://viking.acme.corp (team deployment with TLS terminator)

OPENVIKING_API_KEY

Values: Any non-empty string. Sent as X-API-Key header on every request.

When needed: when your Viking server has auth_mode: api_key configured. In single-user dev mode, leave empty.

Rotation: Restart OpenSpace after updating the value. No hot reload — this is intentional (hot-reloading secrets introduces race windows for little gain).

OPENVIKING_NAMESPACE

Values: Team/tenant identifier (alphanum, -, _, .). Sanitized on read.

Effect on URIs:

Without namespace:       viking://agent/memories/tools/
With namespace=acme:     viking://tenants/acme/agent/memories/tools/

Never auto-derived — if unset, URIs are global. To prevent accidental cross-team memory mixing, namespace must be set explicitly.

Deployment patterns:

  • Single team/individual: leave empty
  • Team deployment: set in shared .env file, docker-compose, systemd unit, etc.
  • Multi-team shared Viking: each team's deploy sets its own value
  • CI-per-repo: derive in CI config (not inside OpenSpace) and export before running

OPENVIKING_USER_ID

Values: Any string (sanitized to alphanum + -_.).

Fallback chain (first non-empty wins):

  1. OpenSpaceConfig.openviking_user_id
  2. OPENVIKING_USER_ID env var
  3. USER env var (Unix/macOS)
  4. USERNAME env var (Windows)
  5. Empty string → uses team-global user memory path

Why auto-fallback to OS user: On single-machine dev, per-user isolation should work without configuration. If two people share the same OS user, they should explicitly set OPENVIKING_USER_ID to their identity.

Effect on URIs:

Without user_id:             viking://tenants/acme/user/memories/preferences/
With user_id=alice:          viking://tenants/acme/user/alice/memories/preferences/

OPENVIKING_PUSH_SKILLS

Values:

  • Truthy: 1, true, yes, on
  • Falsy: 0, false, no, off
  • Anything else → falls back to OpenSpaceConfig.openviking_auto_push_skills (default True)

Effect: Controls whether evolved SKILL.md content is pushed to Viking as a structured resource via POST /api/v1/skills. Session feedback is unaffected — task descriptions and tool sequences are still committed regardless.

When to set false:

  • Skills may contain sensitive customer data or credentials
  • Team policy forbids sharing evolved skill content across agents
  • Testing skill evolution in isolation without polluting team Viking
  • Regulatory compliance (data residency, etc.)

Priority: env var overrides config, but only if value is recognized. Unknown values (e.g. OPENVIKING_PUSH_SKILLS=maybe) fall back to config default — no surprise off-state.

OPENVIKING_MIN_SCORE

Values: Float in [0.0, 1.0]. Out-of-range or unparseable values fall back to the config default.

Effect: Viking /api/v1/search/find results with a score below this threshold are dropped in two places:

  1. Server-side — passed as the score_threshold field in the request payload so Viking never ships the hit back.
  2. Client-side safety netfind_memories() in client.py filters the response again before returning, guarding against server versions that ignore the field.

When to set:

  • 0.0 (default) — accept everything, maximum recall
  • 0.5 — typical quality-oriented deployment
  • 0.7+ — high-precision deployments where wrong memories are very expensive

Measuring impact: result["viking"]["hit_counts"] will show fewer hits as the threshold rises; correlate with actual task quality over time.

OPENVIKING_SCRUB_PII

Values:

  • Truthy: 1, true, yes, on
  • Falsy: 0, false, no, off
  • Anything else → falls back to config default

Effect: When enabled (the default), every user-authored string written to Viking via feedback_evolution(), feedback_negative(), or push_evolved_skills() passes through the regex scrubber in scrubber.py before being sent.

Redacted patterns:

Category Pattern coverage
API keys Anthropic (sk-ant-*), OpenAI (sk-proj-*, sk-*), OpenRouter (sk-or-*), GitHub (ghp_*, github_pat_*), AWS (AKIA*, ASIA*), GCP (AIza*), Slack (xox*)
Tokens JWT (3 base64 segments), Authorization: Bearer ...
Credentials Basic-auth URLs (https://user:pass@host), RSA/EC/OpenSSH private key blocks
PII Email addresses, phone numbers (strict E.164 only), SSN, IP addresses
Financial Credit card numbers (Luhn-validated — non-valid numbers are left alone)

When to set false: Only when you fully trust the Viking endpoint and task content. Disabling the scrubber is a privacy/security decision — the scrubber itself adds negligible latency (~microseconds per string).

Idempotent: Scrubbing already-scrubbed text produces the same result, so re-processing is safe.


OpenSpaceConfig fields

All env vars have corresponding config fields for programmatic access:

from openspace.tool_layer import OpenSpaceConfig, OpenSpace

config = OpenSpaceConfig(
    llm_model="openrouter/anthropic/claude-sonnet-4.5",

    # OpenViking integration
    openviking_enabled=True,           # bool
    openviking_url="",                 # str, "" = use env
    openviking_api_key="",             # str, "" = use env
    openviking_namespace="acme",       # str
    openviking_user_id="alice",        # str, "" = fallback chain
    openviking_auto_push_skills=True,  # bool
    openviking_min_score=0.0,          # float, 0.0–1.0 (Round 6)
    openviking_scrub_pii=True,         # bool (Round 6)
    openviking_mid_iter_tool=True,     # bool — expose retrieve_memory tool (Round 6)
)

async with OpenSpace(config) as os:
    result = await os.execute("my task")

    # Round 6: explicit user feedback API
    await os.provide_feedback(
        task_id=result["task_id"],
        polarity="positive",  # or "negative"
        comment="User confirmed the response was correct",
    )

OpenSpace.provide_feedback() public API (Round 6)

async def provide_feedback(
    self,
    task_id: str,
    polarity: str,            # "positive" or "negative"
    comment: str = "",
    task_description: str = "",
    tool_sequence: Optional[List[str]] = None,
) -> Dict[str, Any]

Explicit feedback channel for host agents and communication adapters that capture user signals ("that was wrong", "try again", thumbs-up/down). Routes to the right Viking memory bucket:

  • polarity="positive" → creates a reinforcement session openspace-rating-<task_id>
  • polarity="negative" → creates an anti-pattern session openspace-neg-<task_id> that lands in agent/memories/antipatterns/

Returns {"status": "ok"|"skipped"|"error", "polarity": str, "session_id": str}. Best-effort: if Viking is down, returns {"status": "skipped"} instead of raising.

Precedence: Explicit config value > env var > fallback. An empty string in config means "use env var". A non-empty string in config means "ignore env var for this field".


Identity resolution: the full story

The function resolve_viking_identity() is the single source of truth. Its signature:

def resolve_viking_identity(
    namespace_override: str = "",
    user_id_override: str = "",
) -> tuple[str, str]:

Resolution logic:

namespace:
    config_override          (if non-empty)
    → OPENVIKING_NAMESPACE env
    → "" (empty = no tenant scoping)

user_id:
    config_override          (if non-empty)
    → OPENVIKING_USER_ID env
    → USER env (Unix)
    → USERNAME env (Windows)
    → "" (empty = team-global user path)

Both values are sanitized — only alphanum plus -_. survive. Leading/trailing -_. are stripped.

Examples:

config ns config uid env NS env UID env USER Resolved
"" "" "" "" jimmy ("", "jimmy")
"acme" "" "" "" jimmy ("acme", "jimmy")
"" "" "acme" "alice" jimmy ("acme", "alice")
"override" "" "env-ns" "" jimmy ("override", "jimmy")
"" "override" "" "env-uid" jimmy ("", "override")
"acme corp!" "alice@corp" ("acmecorp", "alice.corp")

The last row shows sanitization: space and ! are stripped from namespace; @ is stripped from user_id but . is preserved.


URI model

All Viking memory queries use directory-prefix URIs. The client builds them via agent_memory_uri(category) and user_memory_uri(category):

Agent memories (team-shared)

c = OpenVikingClient(namespace="acme")
c.agent_memory_uri("tools")     # viking://tenants/acme/agent/memories/tools/
c.agent_memory_uri("patterns")  # viking://tenants/acme/agent/memories/patterns/
c.agent_memory_uri("skills")    # viking://tenants/acme/agent/memories/skills/
c.agent_memory_uri("cases")     # viking://tenants/acme/agent/memories/cases/

Without namespace, these fall back to:

viking://agent/memories/tools/
viking://agent/memories/patterns/
...

User memories (per-user)

c = OpenVikingClient(namespace="acme", user_id="alice")
c.user_memory_uri("preferences")  # viking://tenants/acme/user/alice/memories/preferences/
c.user_memory_uri("profile")      # viking://tenants/acme/user/alice/memories/profile/

With namespace but no user_id:

viking://tenants/acme/user/memories/preferences/

With neither:

viking://user/memories/preferences/

Custom URI queries

If you need to query a custom prefix:

results = await client.find_memories(
    query="my search",
    target_uri="viking://some/custom/path/",
    limit=10,
)

find_memories is the low-level primitive; find_tool_knowledge, find_cases, etc. are thin wrappers that compute target_uri from the identity config.


Deployment patterns

Pattern 1 — Single-machine dev (you, alone)

# .env
OPENVIKING_ENABLED=true
# everything else empty — $USER provides user isolation

Pattern 2 — Small team (2–5 devs, shared Viking on LAN)

# Shared team .env (checked into team-private config repo)
OPENVIKING_URL=http://viking.internal:1933
OPENVIKING_API_KEY=<team-key>
OPENVIKING_NAMESPACE=acme-eng

# Per-developer override in ~/.config/openspace/.env
OPENVIKING_USER_ID=alice   # optional — defaults to $USER

Agent memories (tool knowledge, patterns, cases) are shared across the whole team. Alice's preferences don't leak to Bob.

Pattern 3 — Multi-team shared Viking (one server, many teams)

# Team A
OPENVIKING_URL=http://viking.corp.internal:1933
OPENVIKING_NAMESPACE=team-frontend
OPENVIKING_API_KEY=<team-frontend-key>

# Team B
OPENVIKING_URL=http://viking.corp.internal:1933
OPENVIKING_NAMESPACE=team-backend
OPENVIKING_API_KEY=<team-backend-key>

Each team has isolated memory namespace. Viking's own RBAC enforces API key scoping.

Pattern 4 — CI / ephemeral environments

# .github/workflows/test.yml
env:
  OPENVIKING_ENABLED: false  # don't pollute team memory from CI runs

Or point CI at a dedicated Viking instance:

env:
  OPENVIKING_URL: http://ci-viking:1933
  OPENVIKING_NAMESPACE: ci-runs
  OPENVIKING_PUSH_SKILLS: false  # don't push ephemeral skills

Pattern 5 — Privacy-sensitive production

OPENVIKING_ENABLED=true
OPENVIKING_URL=http://internal-viking
OPENVIKING_NAMESPACE=prod
OPENVIKING_PUSH_SKILLS=false   # no skill content leaves the box
OPENVIKING_USER_ID=<bot-account>

Session feedback (task, tools) still commits for memory extraction, but evolved skill content stays local.


Observability configuration

Every execute() returns a viking dict in the result:

result["viking"] == {
    "enabled": bool,
    "available": bool,
    "query": str,
    "enrichment_chars": int,
    "hit_counts": {
        "tool_hints": int,
        "pattern_hints": int,
        "skill_hints": int,
        "user_preferences": int,
        "case_hints": int,
    },
    "selector_hints_chars": int,
    "analysis_context_used": bool,
    "feedback_status": "skipped" | "attempted" | "committed" | "failed" | "disabled",
    "pushed_skills": int,
}

Plus a single-line log per task:

Viking telemetry: available=True hits=9 enrich_chars=1243 feedback=committed pushed=1

Grep-friendly for quick dashboards. See Token Economics — Measuring in production.


.env.example reference

The canonical template lives at openspace/.env.example. Relevant section:

# ── OpenViking Integration (optional) ───────────────────────
# Connect to OpenViking context database for cross-session memory.
# If OpenViking server is not running, OpenSpace works normally without it.
# OPENVIKING_URL=http://127.0.0.1:1933
# OPENVIKING_API_KEY=
# OPENVIKING_ENABLED=true

# Multi-team / multi-user scoping (optional):
# OPENVIKING_NAMESPACE=
# OPENVIKING_USER_ID=

# Skill resource push (privacy toggle):
# OPENVIKING_PUSH_SKILLS=true

# Retrieval quality + privacy hardening (Round 6):
# OPENVIKING_MIN_SCORE=0.0
# OPENVIKING_SCRUB_PII=true

Common misconfigurations

"Why isn't Viking doing anything?"

Check in order:

# 1. Is integration enabled?
print(config.openviking_enabled)  # should be True

# 2. Is the client constructed?
print(openspace._viking_client)  # should not be None

# 3. Is the server reachable?
available = await openspace._viking_client.is_available()
print(available)  # should be True

If (3) fails, check:

  • curl http://127.0.0.1:1933/health from the OpenSpace host
  • Firewall rules between OpenSpace and Viking
  • OPENVIKING_URL has correct port (default 1933, not some other Viking port)

"User memories are leaking between users on a shared machine"

Set OPENVIKING_USER_ID explicitly per user. The $USER fallback uses the OS user name, so if two humans share the same OS account, they share the same user_id.

"Team memories are mixing with my personal experiments"

You probably set OPENVIKING_NAMESPACE in your global .env then did personal work without unsetting it. Either:

  • Use different Viking instances (dev vs team)
  • Set OPENVIKING_NAMESPACE=personal-$USER in your shell rc before personal work
  • Start the namespace with a sentinel prefix so team deploys won't accidentally match

"Skills are being pushed when I don't want them to"

OPENVIKING_PUSH_SKILLS=false is the switch. Note that session feedback still commits — this is intentional (extracted abstracts are low-risk, SKILL.md content is higher-risk).

If you want to disable ALL feedback too, the cleanest path is to disable integration entirely: OPENVIKING_ENABLED=false.


HTTP client tuning

Not user-configurable today, but documented for contributors:

Parameter Value Location
Request timeout 5.0 seconds _REQUEST_TIMEOUT in client.py
Health cache TTL (success) 60 seconds _HEALTH_CACHE_TTL_OK
Health cache TTL (failure) 12 seconds _HEALTH_CACHE_TTL_FAIL
Max query length 500 chars _MAX_QUERY_CHARS in enrichment.py
Memory fetch limit per category 3–5 (tool/pattern/skill=5, pref/case=3) inline in enrichment.py

If you need to tune these, fork and edit the constants. They are intentionally not exposed as config knobs — the defaults are chosen to keep worst-case latency bounded without user ceremony.


Next: Operations — business rules, deployment, testing, troubleshooting.