-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconfig.yaml
More file actions
483 lines (460 loc) · 22.2 KB
/
Copy pathconfig.yaml
File metadata and controls
483 lines (460 loc) · 22.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
# project mocha — Central Configuration
# All services read from this file.
# ---------------------------------------------------------------------------
# Auth — JWT secret for user authentication.
# The secret is read from the JWT_SECRET env var (set in .env, which is
# gitignored); leave this blank so no credential is ever committed. Only used
# as a last-resort fallback if JWT_SECRET is unset.
# ---------------------------------------------------------------------------
auth:
jwt_secret: ""
# ---------------------------------------------------------------------------
# Network — single place to set addresses for LAN / WAN deployment.
# ---------------------------------------------------------------------------
network:
# Public-facing IP or hostname (LAN, WAN, or Tailscale).
# Used in logs only; browser clients use window.location.hostname automatically.
external_host: "192.168.0.63"
# Address for service-to-service calls (all services on this machine).
# Only change if you split services across multiple machines.
internal_host: "127.0.0.1"
llm:
# Mocha's OWN small/fast model (Llama-3.2-3B on vLLM, GPU :8893), served by
# project-mocha-vllm.service. She no longer shares opus's 32B — faster replies,
# a friendlier voice, and total inference isolation from the trading desk.
# The rate-limit + circuit-breaker in bridge/llm_client.py still applies.
base_url: http://127.0.0.1:8893/v1
model: unsloth/Llama-3.2-3B-Instruct
# Note: no generic system_prompt here — character/soul.md is the single source
# of truth for Mocha's voice. A generic framing block here would dilute it.
temperature: 0.8
max_tokens: 2048
# Optional: set if vLLM was started with --api-key
# api_key: ""
request_timeout_s: 360
# If any segment exceeds this word count, run repair/split.
repair_max_words_per_segment: 60
# Isolation guard for the SHARED opus-trading vLLM (bridge/llm_client.py).
# Bounds Mocha's worst-case load so a Mocha bug can never starve Corvus's
# trading inference. Defaults shown; tune only if needed.
isolation:
# FAST client = Mocha's DEDICATED 3B (:8893). Nothing to protect here, so
# don't rate-limit it (that was adding multi-second waits under load). The
# strict guard lives on the `deep` client below (the shared trading vLLM).
max_concurrency: 3 # allow a little parallelism on the dedicated 3B
rate_limit_per_min: 0 # 0 = no rate cap
burst: 1
breaker_fail_threshold: 4 # still fail fast if the 3B hangs
breaker_cooldown_s: 15
# ── Model routing ──────────────────────────────────────────────────────────
# Deep model for reasoning/tool-heavy turns — opus's SHARED Qwen-32B (:8000).
# The isolation guard above protects this shared path too. Remove this block
# to disable routing (everything stays on the fast 3B).
deep:
base_url: http://127.0.0.1:8000/v1
model: Qwen/Qwen3-32B-FP8
# Bigger budget than the 3B's 2048: Qwen *thinks* (a <think> block) before
# answering, and on a hard/escalated reasoning turn 2048 truncates mid-thought
# → no answer reaches TTS (the empty-fallback fires). 4096 lets think+answer fit.
max_tokens: 4096
# STRICT guard — this path hits the SHARED trading vLLM, so bound Mocha's
# load so a Mocha bug can never starve the desk (the prime directive).
isolation:
max_concurrency: 1
rate_limit_per_min: 30
burst: 8
breaker_fail_threshold: 4
breaker_cooldown_s: 20
# Heuristic router (no extra LLM call → no TTFT cost): default to the fast 3B;
# escalate to deep on these signals. Tool turns also auto-escalate (synthesis).
routing:
enabled: true
min_words: 40
keywords: [trading, desk, position, portfolio, pnl, "p&l", nav, risk, alloc,
conviction, attribution, briefing, hedge, exposure, drawdown,
earnings, catalyst, stock, ticker, price, market, analyze,
explain, compare, forecast, valuation]
# Tool/lookup intent → route the tool-EMITTING pass to Qwen (not the 3B,
# which fumbles inline tool-call JSON). Kept narrow so casual chat stays on
# the 3B and off opus's shared GPU. Omit to use the in-code default list.
tool_keywords: [news, headline, weather, forecast, temperature, "look up",
lookup, search, google, schedule, remind, reminder, calendar,
diary, "show me", "pull up", "what's the", "whats the",
"how much", "how many", "price of", latest, "who won", score,
stock, chart, "play "]
# Output verifier — on NON-realtime surfaces (telegram/discord/cli, cron, and
# the eval harness) Qwen re-reads the drafted reply before it's sent and repairs
# leaked artifacts (<think>, raw JSON, stray tags) + claims not backed by the
# tool results. The webapp (typed + live voice) streams live and skips this.
# Fail-open: any verifier error keeps the original draft. See graph.py:verify_node.
verifier:
enabled: true
realtime_sources: [web, ws_live, voice, voice-stream]
# The verifier only fires when a tool ran (data fidelity) or the draft looks
# malformed (leaked <think>/JSON/tags). Clean tool-free prose is sent as-is,
# so her voice on personal replies is never reprocessed.
stt:
# Voice INPUT removed (text-only) to free ~5.5GB VRAM for Mocha's own LLM.
# start.sh no longer launches it; health checks skip it when disabled. Set
# true + re-add the start_service line in start.sh to bring voice input back.
enabled: false
host: 0.0.0.0
port: 8091 # remapped 8001->8091: opus trading owns 8001 (llm-proxy)
model: large-v3
device: cuda
compute_type: float16
# GPU assignment: all services on Blackwell (GPU 0)
gpu_id: 0
language: en
# Set to null for auto-detect
# language: null
# WS /ws/stream — VAD utterance detection (optional; defaults shown)
# vad_aggressiveness: 2
# vad_silence_ms: 450
# vad_min_speech_ms: 200
# vad_max_utterance_ms: 30000
tts:
host: 0.0.0.0
port: 8092 # remapped 8002->8092: opus trading owns 8002 (vllm-embed)
engine: f5-tts
# cuda | cpu — use cpu if TorchCodec/CUDA (e.g. libnvrtc) breaks on your machine
device: cuda
# When set, skips Whisper ASR inside F5-TTS (faster, fewer deps, stable latency).
# Pulled from your previous TTS preprocessing log for `audio/reference_voice.wav`.
reference_text: "Cheap hobbies are a must for the young man. If you'd like to learn the paper etiquette, I'd be happy."
# Optional: exact transcript of reference_audio. When set, skips Whisper ASR inside F5-TTS (faster, fewer deps).
# reference_text: "Your line matching reference_voice.wav"
# Reference audio for voice cloning (place a 6-15s clean clip here)
reference_audio: audio/reference_voice.wav
# GPU assignment: all services on Blackwell (GPU 0)
gpu_id: 0
# Classifier-free guidance strength. F5-TTS default is 2.0.
# Lower (1.5–1.8): more variation, can sound dramatic / shouty on "!"
# Default (2.0): F5's stock balance
# Higher (2.2–2.6): leans harder on the calm reference voice; dampens
# prosody peaks (the "!" pitch spike, ALL CAPS energy).
# Too high → starts to sound monotone.
# Bridge also strips "!" → "." before sending text to TTS, so this knob is
# mostly belt-and-suspenders for ALL CAPS / strong emphasis cases.
cfg_strength: 2.2
memory:
# In-process mem0 store (no HTTP service). See memory/mem0_store.py.
# mem0 handles fact extraction, entity linking, dedup, and time-aware
# recall. Fact-extraction LLM calls go to the same vLLM instance as Mocha
# (llm.base_url above) and are offloaded to a thread so TTFT is preserved.
backend: mem0
chroma_path: memory/mem0_chroma
history_db: memory/mem0_history.sqlite
collection: parrot_mem0
embedding_model: all-MiniLM-L6-v2
max_results: 5
# How many recent exchanges to keep in short-term context
short_term_limit: 22
# Single-operator assistant: every channel funnels into ONE user_id — Ika —
# so memory, history, and shared-news records all share one namespace and a
# Telegram reply lands where the news was stored. (`channel` stays in metadata
# for filtering.) bridge/server.py:IKA_USER_ID reads this value.
mem0_user_id: ika
# ---------------------------------------------------------------------------
# diary — Mocha's per-day activity memory ("her diary")
# Writes a per-day page summarizing tool activity + interactions in Mocha's
# voice. Draft while today; finalized at local-tz midnight.
# ---------------------------------------------------------------------------
diary:
enabled: true
data_dir: data/diary # YYYY-MM-DD.json files
chroma_path: memory/diary_chroma # embedding store for semantic search
# Writer is only triggered if the user has been idle this long:
idle_threshold_minutes: 10
# And only once every this often (min wait between draft writes):
min_interval_minutes: 10
web:
port: 8080
# Public URL of the trading-desk dashboard. Mocha's app shows an "Enter trading
# desk" button linking here. After the tunnel swap the desk lives at mocha.* and
# Mocha (this app) is the apex. Leave blank to hide the button.
desk_url: "https://mocha.project-hello-mocha.com"
animation:
# fbx_functions — LLM picks from gesture functions defined in
# character/animation_functions.csv. Pre-converted FBX clips are
# played by the web app's animation controller (Start/Loop/End).
mode: fbx_functions
crossfade_ms: 250
idle_fidget_interval: [8, 15]
bridge:
host: 0.0.0.0
port: 8090 # remapped 8000->8090: opus trading owns 8000 (vllm)
ws_port: 8090
# Internal service URLs — auto-derived from network.internal_host + each
# service's port when omitted. Set explicit URLs only to override.
# stt_url: http://127.0.0.1:8091
# tts_url: http://127.0.0.1:8092
# llm_url: http://127.0.0.1:8000 # opus trading's shared vLLM (see llm.base_url)
# Memory is in-process (memory/mem0_store.py); animation is CSV-driven
# (character/animation_functions.csv, mode=fbx_functions below).
# Phone-call mode: WS /ws/live — continuous PCM uplink + VAD utterance detection → STT → LLM
live:
enabled: true
# How to handle the assistant's own audio while the mic is streaming.
# "room": your mic hears the speaker, so we mute mic during TTS playback
# to prevent the "talking to itself" acoustic loop.
# "headphone": your mic does not hear the speaker, so we allow mic during TTS
# for easier barge-in.
echo_mode: room
# If set, overrides echo_mode.
# true => mute mic while agent is talking (recommended for room mode)
# false => do not mute (recommended for headphone mode)
mute_mic_while_agent_talking: null
sample_rate: 16000
frame_ms: 20
vad_aggressiveness: 2
silence_ms: 450
min_speech_ms: 200
max_utterance_ms: 30000
# ---------------------------------------------------------------------------
# Channels — messaging surfaces that connect to the bridge.
# Each channel POSTs to /channel and relays the text reply.
# ---------------------------------------------------------------------------
channels:
telegram:
enabled: true
# Bot tokens are stored per-user in the database (settings.telegram_bot_token).
# Use PUT /api/user/telegram to register a bot, or run auth/migrations.py
# to seed the ika_admin superuser with the default token.
discord:
enabled: false
bot_token: ""
# Optional: restrict to specific guild/channel IDs.
# allowed_guilds: [123456789]
# allowed_channels: [123456789]
cli:
enabled: true
# ---------------------------------------------------------------------------
# Tools — CLI / filesystem tools the LLM can call via function-calling.
# Only active on the /channel endpoint (text channels), NOT on /chat or /voice
# to avoid adding latency to the real-time voice pipeline.
# ---------------------------------------------------------------------------
tools:
enabled: true
allowed:
# Research & data — Mocha calls these directly (Nori sub-agent removed).
- web_search
- get_stock_data
- get_news
- get_weather
- calculate
# UI panels (Mocha renders her own findings)
- show_slides
- control_slides
- show_card
- show_notification
- show_weather # iPhone-Weather-style modal with animated bg
- video_player # Mocha finds real IDs via web_search before playing
- clear_ui
# Cron + autonomy (Mocha schedules user-asked reminders / routines)
- schedule_cron
- list_cron_jobs
- cancel_cron_job
- mocha_self_mute
# Mocha's diary (per-day activity memory)
- recall_diary
- show_diary
# Recall news she's SHARED before (semantic search over shared_news mem0)
- recall_news
# Save the user's preferred name once they introduce themselves
- set_display_name
# Personalized trading desk (proxies opus trading MCP — read-only, PG-only).
- get_trading_briefing
- get_position_dossier
- get_ticker_valuation # desk's Damodaran/Sonnet intrinsic-value verdict, distilled (hand-written wrapper)
- get_position_brief # one call = position dossier + valuation, for strategy questions (hand-written wrapper)
- get_agent_overview
- get_pnl_attribution
- get_trade_activity
- get_risk_overview
- get_agent_disagreement
- get_position_history
- get_changes_since
- get_upcoming_catalysts
- get_manual_overrides
# Desk-state read-outs (read-only) — keep in sync with _opus_introspect.py.
- get_pnl_summary
- get_positions
- get_balances
- get_agent_pnl_windows
- get_trade_blotter
- get_agent_list
- get_market_status
- get_kill_switch_status
# Shared knowledge graph (read-only) — cited entity relationships
# (e.g. AMZN —invested_in→ Anthropic). interpret_node also consults
# kg_query directly; exposing them lets the LLM query relationships too.
- kg_query
- kg_neighbors
working_dir: /home/tianyizhang/opus trading/project_mocha
max_rounds: 5
timeout: 120 # generous: a tool round may need time for multi-step data fetches
# Shell-execution safety. Substring match; case-sensitive. Applies to all
# callers of bash_exec (bash_exec is not in Mocha's allowlist — no shell access).
bash_exec:
blocklist_patterns:
- "rm -rf /"
- "rm -rf /*"
- "mkfs"
- "dd if="
- ":(){ :|:&};:"
- ":(){:|:&};:"
- "> /dev/sd"
- ">/dev/sd"
- "chmod -R 777 /"
# ---------------------------------------------------------------------------
# Task runtime — cross-turn "what is she doing" state machine (Stage 1).
# OFF by default: when disabled the conversational graph behaves exactly as
# before. When on, task intents (currently: play_media) run through a persistent
# fulfill loop so an intent carries across turns and the primary tool is called
# deterministically. See docs/task_runtime_design.md. Requires tools.enabled.
# ---------------------------------------------------------------------------
task_runtime:
enabled: false
park_ttl_seconds: 600 # drop a parked task (awaiting an answer) after this idle
# ---------------------------------------------------------------------------
# Agent loop — proactive background scheduler that runs tasks on a cron and
# broadcasts results through registered channels.
# ---------------------------------------------------------------------------
agent_loop:
enabled: true
persist_file: data/cron_jobs.json
jobs:
- id: morning_greeting
cron: "0 8 * * *"
action: prompt
params:
text: "Good morning! Give me a brief, friendly morning greeting."
description: Morning greeting sent to all channels
enabled: false # disabled by default; flip on once you want the daily 8am ping
# ---------------------------------------------------------------------------
# Autonomy — lets Mocha initiate conversation (reconnect hello, drift-on-topic,
# bored / lonely check-ins). Requires autonomy_engine wired through
# _idle_heartbeat_loop. Routes speech to web UI if connected, else Telegram
# (primary user learned from first /channel POST), else queues for next reconnect.
# ---------------------------------------------------------------------------
autonomy:
enabled: true
# Seconds of silence before Mocha starts considering a drift riff on the
# current topic. LLM decides whether to actually speak; returning
# {"segments":[]} means "stay quiet this tick".
drift_after_s: 45
# After this many seconds, escalate to bored (playful, one-shot check-in).
bored_after_s: 120
# After this many seconds, escalate to lonely (slightly more emotionally honest).
lonely_after_s: 300
# Minimum seconds between LLM-driven autonomy evaluations (decides whether
# to speak). Keeps cost bounded.
eval_interval_s: 90
# Hard floor between any two SPOKEN autonomous turns (news share or check-in).
# Raised 180 -> 600 so she's genuinely rare, not chatty.
min_interval_between_autonomous_turns_s: 600
# Safety ceiling — counter is now PERSISTED to data/autonomy_state.json so it
# actually survives bridge restarts (it used to reset to 0 on every restart,
# which is how a churny day produced 70+ check-ins against a "12" cap).
daily_max_autonomous_turns: 10
# Probability a bored/lonely fallback check-in fires when the curiosity pool is
# empty AND allow_empty_checkins is on. Lower = more silence.
checkin_probability: 0.5
# Reconnect "welcome back" greetings: their OWN budget, separate from news, so a
# storm of web reconnects (tab focus, network blips, deploys) can't spam them or
# starve the news budget. At most one per debounce window, capped per day.
reconnect_debounce_s: 1800 # ≤ one greeting per 30 min
reconnect_max_per_day: 4
# Presence gate (autonomy/presence.py) — the load-bearing "don't talk over me"
# knob. Mocha is only proactive once Ika has been silent (no message, no
# in-flight turn) for this long; the instant he speaks she's CONVERSING and
# silent, and after idle_after_s of quiet she returns to IDLE. This replaced
# the old never-armed task_quiet_after_s guard.
idle_after_s: 600
# When the curiosity pool is empty, may she still fire a content-free check-in?
# Default OFF — she only speaks unprompted when she has a genuinely fresh item,
# never ambient "still there?" filler.
allow_empty_checkins: false
# Mood — a soft emotional prior from the desk's COMBINED day P&L (read-only,
# cached). Colors her tone (brighter up, more careful down) in BOTH the chat
# graph and the idle composer; she never recites P&L numbers unprompted.
mood:
enabled: true
refresh_s: 180 # min seconds between read-only get_balances reads
# Per-mode toggles so you can disable any single autonomy mode without
# killing the whole feature.
modes:
drift: true
bored: true
lonely: true
reconnect_hello: true
# Curiosity — when idle, surface a genuinely interesting news item instead of
# content-free "it's been quiet" filler. Items are fetched via the get_news
# tool, deduped against a seen-set, and pulled from a rotating MIX of her-taste
# topics (science/space/odd) and desk-relevant topics (markets/macro).
# Fail-safe: any error here degrades to "no item" and never breaks the tick.
curiosity:
enabled: true
refill_interval_s: 2700 # ~45 min between Brave refills
pool_target: 6 # only refill when fewer than this remain unseen
fetch_per_topic: 5 # articles requested per refill
max_seen: 500 # cap on the dedup ledger
topics_self:
- "latest space exploration discovery"
- "new neuroscience study brain"
- "physics discovery this week"
- "recent archaeology discovery"
- "strange offbeat science news"
- "deep sea creature discovery"
- "AI research breakthrough this week"
- "new astronomy discovery telescope"
topics_desk:
- "stock market today"
- "federal reserve interest rates"
- "technology company earnings"
- "global economy outlook"
# Notifications queue — deliveries that were fired by cron jobs while no
# surface was reachable (web closed, Telegram unknown). Consumed on next
# client_hello from the web UI.
notifications:
path: data/notifications.jsonl
max_retained_delivered: 200
# Call logging — write-only PostgreSQL log of every LLM call.
# Used for offline evaluation / analytics; the agent never reads this table.
call_log:
enabled: true
dsn: "postgresql://mocha:[email protected]:5432/mocha"
pool_min: 1
pool_max: 5
# ---------------------------------------------------------------------------
# Quota — daily LLM token cap. Registered users are unlimited; anonymous
# guests are capped to keep cost bounded and to give the sign-up CTA teeth.
# Both values may be set to null for unlimited. Cap is on total_tokens
# (prompt + completion), summed from llm_call_log over the local-day window.
# ---------------------------------------------------------------------------
quota:
anon_daily_token_cap: 150000
registered_daily_token_cap: null
# Idle heartbeat — makes the character feel alive when no one is talking.
# The bridge sends silent idle_action messages (emotion + gesture, no audio)
# to Unity after the conversation has been quiet for a while.
idle:
enabled: true
# Seconds of silence before the first idle action fires
initial_delay: 40
# Seconds between subsequent idle actions (randomised ±30%)
interval: 15
# Stop sending idle actions after this many seconds of total silence
# (character "falls asleep" / goes to a resting pose). 0 = never stop.
max_idle_duration: 300
# A connected web tab counts as the live surface for proactive/idle messages
# only if it's been interacted with within this window; past it, the tab is
# treated as asleep and idle news routes to Telegram (the phone) instead of a
# screen no one's watching. 0 = any open tab always counts (old behavior).
web_active_window_s: 7200 # 2 hours
# External API keys used by custom tools.
# Prefer exporting these as environment variables (POLYGON_API_KEY, ...);
# the values here are a fallback so leave them empty in the committed file.
api_keys:
polygon: "" # https://polygon.io (rebranded "Massive"); used by polygon_* tools