- Remove the keyword-rules agent from typed chat: fallback_agent, parse_look_command (+ tables), the copy-edit parser and _rules_check are gone. If every model fails: 'The assistant is busy, please try again in a moment.' with a Retry button (agent 'unavailable', no actions). Clicks, forms and payments stay deterministic (fallback_event); the landing view is a deterministic featured-listings panel; the Sell button opens the empty form. - Local model via llama.cpp llama-server directly (ops/local-llm-watchdog.sh, loopback only, restart on exit/hang, crash backoff, log rotation; deploy.sh ensures it runs). Lean: no vision projector, no weight repack (mmapped), ctx 6144, 1 slot, q8 KV, 6 threads at nice 10, poll 0, 1 GiB prompt cache. Idle 0% CPU, 2.9 GB RSS (0.6 GB private). - Local requests use a JSON GBNF grammar, worked examples in the cached system prompt, and a labelled visitor message. 10/10 correct on the real prompt, warm 4-13 s. - Primer re-primes after a model-server restart (llama-server /metrics counter) and otherwise stays idle. - Fix: hosted calls kept the Authorization header off when a local model was configured (cfg['local'] is a dict on hosted configs); now only the local server itself skips the key. Test added. - Badge/chat header: 'AI: busy', 'AI: quota used up', 'AI: unavailable'; no 'built-in'/'checked by rules' texts. - Tests: proxy checks expect the busy reply (never rules), clicks still work while busy, no-model server, local grammar/ prompt/no key; browser checks for busy + Retry; e2e_http accepts busy for typed turns unless REQUIRE_LLM=1; test_copyedit.py replaced by test_copy_defaults.py; README updated.
38 lines
2.3 KiB
Python
38 lines
2.3 KiB
Python
"""Editable page text (copy slots): the defaults and what a visitor currently sees.
|
||
|
||
Used to tell the model what each text slot shows right now (PAGE_TEXT in the prompt). Text edits themselves are
|
||
made by the model with set_theme copy; there is no rule-based text editing.
|
||
"""
|
||
import os
|
||
|
||
# Keep in sync with copyDefaults in web/src/App.tsx (tests/test_copy_defaults.py checks this).
|
||
DEFAULT_COPY = {
|
||
"logo": "Agent Market",
|
||
"headline": "A marketplace you can just talk to.",
|
||
"subhead": "Tell the agent what you want. It searches, compares, lists your stuff and runs the checkout, right here on the "
|
||
"page. You can also restyle the whole page from the chat.",
|
||
"footer": "Agent Market demo · fake test data · test-mode payments · open API",
|
||
"chips": [{"label": "🚲 Show me bikes", "say": "show me bikes"}, {"label": "📐 Tutoring", "say": "find tutoring services"},
|
||
{"label": "⚖️ Compare tutoring", "say": "compare the two tutoring options"}, {"label": "➕ Sell something", "event": "sell"},
|
||
{"label": "🎨 Make it look retro", "say": "make it look retro"}],
|
||
"bullets": ["{fee}% facilitation fee when you pay online (test mode)", "$0 fee when you meet and pay cash in person",
|
||
"Agents welcome: other AI agents buy and sell through the open API (see For agents)"],
|
||
}
|
||
|
||
|
||
def current_copy(theme, fee_bps=None):
|
||
"""The text each editable slot shows right now for this visitor (defaults + their overrides)."""
|
||
fee_bps = fee_bps if fee_bps is not None else int(os.environ.get("AGENT_MARKET_FEE_BPS", "500"))
|
||
fee = f"{fee_bps / 100:.{1 if fee_bps % 100 else 0}f}"
|
||
c = {k: (list(v) if isinstance(v, list) else v) for k, v in DEFAULT_COPY.items()}
|
||
c["bullets"] = [b.replace("{fee}", fee) for b in c["bullets"]]
|
||
over = (theme or {}).get("copy") if isinstance(theme, dict) else None
|
||
for k, v in (over or {}).items() if isinstance(over, dict) else ():
|
||
if k in ("logo", "headline", "subhead", "footer") and isinstance(v, str) and v.strip():
|
||
c[k] = v
|
||
elif k == "chips" and isinstance(v, list):
|
||
c["chips"] = [x for x in v if isinstance(x, dict) and isinstance(x.get("label"), str)]
|
||
elif k == "bullets" and isinstance(v, list):
|
||
c["bullets"] = [x for x in v if isinstance(x, str)]
|
||
return c
|