- Sun movement eastβzenithβwest with dynamic shadows
- Weather effects: rain (4000 particles), snow (2000 particles), thunderstorms (lightning flashes), fog, cloudy skies
- Sun brightness/size adjusts per weather (bright sunny β dim stormy)
- Storm clouds layer (18 dark clouds) for bad weather
- Night: residential buildings become transparent, agents sleep in beds
- Furniture inside buildings: tables at restaurants/bars, desks in offices/schools, seats in cinema/church
- Agent poses: walking, sitting (at tables/desks), sleeping (in beds)
- Bad weather: 70% agents stay indoors; night: all go home to sleep
- Demo mode cycles through full day/night + weather types automatically
- Moon enlarged (10), stars visible from 19:00, brighter after 21:00
- Trees halved in size for better proportion with buildings
- City expanded: WORLD_SIZE 100β130, shadow camera enlarged
- 10 new public buildings (bank, court, gallery, daycare, vet, yoga, pub, etc.)
- 4 new streets for expanded area
- Ollama defaβ¦
Major simulation features:
- Age progression (1 year per 365 sim days) with lifecycle stages
- Death system for 80+ with exponential probability curve
- Divorce mechanic for unhappy marriages (low trust/sentiment/mood)
- Married couples move in together (cohabitation)
- Church wedding ceremonies, hospital births after 9 sim-day pregnancy
- Baby agents born as real citizens, living with parents, autonomous at age 4
- Annual mayor elections with weighted voting (18+ citizens)
- Short-term (weekly) and long-term goals with auto-refresh
- Memory query API: ask any agent about their memories via LLM
- Community score tracking for election candidacy
- UI: lifecycle badges, deceased agents, mayor display, split goals,
memory query input, parent info, new event icons
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
NN training: realistic persona data, graphs, hot-reload endpointβ
- Update all 20 personas in nn_train.py and nn_selfimprove.py to match
enhanced personas.yaml (corrected names, ages, traits, occupations)
- Add persona-specific behavioral patterns: night shift schedule (George),
nightly bar routine (Frank), crush proximity (LilaβElena), taxi wandering
(Omar), overworked doctor (Priya), morning exercise (Marcus/Yuki), etc.
- Add persona-aware needs generation, mood calculation, starting locations
- Add training graph plots (loss + accuracy curves) after training
- Add --graph flag to display graphs from last training run
- Decouple --push from training (push existing model without retraining)
- Add POST /api/nn/reload endpoint for hot-reloading ONNX model from HF Hub
- NNClient.reload() deletes cache, re-downloads, swaps ONNX session in-place
- Both scripts auto-trigger /api/nn/reload after pushing to HF Hub
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add life events, goals, pregnancy system; fix profile editor bugsβ
- Life events: append-only timeline seeded from persona, extended by
LLM reflections and romance milestones (dating/engaged/married/birth)
- Goals: personal aspirations with progress tracking, managed by LLM
reflections (add/complete/progress)
- Pregnancy: married couples can conceive, birth after ~7 sim-days,
baby names, both parents get memories and life events
- Agent context: LLM now sees life story, goals, children, pregnancy
- Fix profile editor: fetch full agent detail instead of WS summary
(occupation, background, personality sliders now load correctly)
- Fix Go dropdown: only rebuild when empty, preserves user selection
- Fix WS state summary: include age, occupation, daily_plan, is_player
- NN self-improve: track accuracy in training_stats.json, compare with
previous model on push
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Gemini uses "quota" in ALL 429 responses, even per-minute rate limits.
The circuit breaker was triggering on the first per-minute 429, declaring
daily quota exhausted and blocking all calls until midnight.
Now only circuit-breaks on "per-day"/"daily" keywords or retry-after > 120s.
Per-minute throttles wait up to 30s and retry normally.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The circuit breaker was arming on any retry-after > 15s, but Groq sends
retry-after: 30-60s for normal per-minute rate limits. This caused
immediate "quota exhausted" on first use with many agents.
Now only circuit-breaks on retry-after > 120s or body containing
"daily"/"limit" keywords. Per-minute throttles wait and retry normally.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add NN provider as default, remove HF inference (7B/0.5B)β
- New NNClient: downloads ONNX model from RayMelius/soci-agent-nn on
first use, runs inference via onnxruntime on CPU (~1ms for 50 agents).
Zero API calls, zero cost, works offline.
- NN is now the default provider β always available, no API key needed.
LLM probability defaults to 100% for NN (no rate limiting).
- Removed HFInferenceClient and all 7B/0.5B model references β these
required HF Pro credits and never worked on free tier.
- Updated provider list in UI, fallback order, and provider icons.
- Added training notebook (notebooks/soci_agent_nn.ipynb).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix Gemini conversations: bump LLM budget to 2 and remove double probability gateβ
With budget=1, action decisions consumed the entire per-tick allowance,
leaving zero budget for conversations. Also, active conversations were
gated by llm_call_probability on every turn β now they always continue
once started.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix HF provider switch: don't raise at init if HF_TOKEN missingβ
HFInferenceClient.__init__ no longer raises ValueError when token is absent.
Logs a warning instead; complete() already returns "" gracefully.
Allows switching to HF models from the UI even without a token configured.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Set soci-agent-7b as default across all providers and UIβ
- HF provider now defaults to RayMelius/soci-agent-q4 (was SmolLM3)
- Ollama provider now defaults to soci-agent (was llama3.1:8b)
- Startup priority: Claude > HF/soci-agent > Groq > Gemini > Ollama
- auto-detect order in create_llm_client matches new priority
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add Soci fine-tuned model and training data pipelineβ
- Add RayMelius/soci-agent-q4 (fine-tuned Qwen2.5-0.5B) to HF provider list
- Add soci-agent as named Ollama option for GGUF-loaded model
- Add MODEL_HF_SOCI and MODEL_OLLAMA_SOCI constants to llm.py
- Persist conversation_history in simulation snapshots (to_dict/from_dict)
- scripts/collect_training_data.py: poll Render API, save raw JSONL
- scripts/convert_to_training_jsonl.py: convert to SFT chat format
- scripts/finetune_local.py: local Unsloth fine-tune (RTX 4050, Windows-safe)
Fixes: transformers 4.56 list_repo_templates patch, TORCHINDUCTOR_DISABLE
Round 1: 116 examples, 3 epochs, loss 1.9222, LoRA -> RayMelius/soci-agent-q4
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix Gemini default: use gemini-2.0-flash-lite (confirmed free-tier working)β
Live testing showed:
- gemini-1.5-flash* models β 404 on current API keys (deprecated/unavailable)
- gemini-2.0-flash-lite β reaches API, only fails when daily quota exhausted
- gemini-2.0-flash-001 β same (valid model, quota-limited)
Update default to gemini-2.0-flash-lite, fallback chain to 2.x models only,
and fix provider label in UI. Switch live Space to Groq (working now).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add _last_error tracking to GeminiClient and GroqClient for diagnosisβ
Both clients previously returned empty string silently on all error paths,
making the /llm/test endpoint show error:"" even when calls were failing.
Now every silent-return path sets _last_error so the test endpoint can
surface the real reason.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix HF provider: require token, hide from menu if absent, fix stuck providerβ
- HFInferenceClient.__init__ now raises ValueError if no token found
(checks HF_TOKEN, HUGGINGFACE_TOKEN, HF_API_TOKEN); prevents silently
creating a broken client that gets stuck as active provider
- /llm/providers: HF models only shown when token is available
- /llm/provider: ValueError from missing credentials returns 400 not 500
- Fix Gemini label: "1.5 Flash" (matches actual default model)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix Gemini model: default to gemini-1.5-flash, robust fallback detectionβ
- Change default from gemini-2.0-flash to gemini-1.5-flash (reliably
available on the OpenAI-compatible endpoint; 2.0 opt-in via GEMINI_MODEL)
- Model-unavailable detection now triggers on ANY non-429 status code
where the body contains an unavailability keyword (not just 400/404),
covering 403/422/etc. that Gemini may return for unsupported models
- Extract keywords into _GEMINI_MODEL_UNAVAILABLE_KWS constant
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix Gemini fallback: add 'serverless' to model-not-found keyword listβ
The Gemini API returns 'serverless' in the error body when a model isn't
available on the OpenAI-compatible endpoint. The fallback detection was
missing this keyword so gemini-1.5-flash was never tried.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Persist LLM probability across restarts; fix Gemini model fallbackβ
- Add `settings` table to SQLite DB for key-value config persistence
- On startup: load saved llm_call_probability from DB (env var still wins);
env var value is also written back so other workstations inherit it
- On slider change: persist new value to DB immediately
- GeminiClient: auto-fallback to gemini-1.5-flash when configured model
returns 400/404 "not found" on the serverless endpoint; tracks unavailable
models per-instance so future calls skip straight to fallback
- Add _GEMINI_FALLBACK_CHAIN and MODEL_GEMINI_FLASH_FALLBACK constants
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add LLM probability slider: controls AI usage per call siteβ
Adds a 0-100% slider (brain emoji, in the toolbar between speed and
zoom controls) that gates every LLM call in the simulation:
- Action decisions, conversation turns, reflections, social starts,
daily plan generation β each rolls random() < llm_call_probability.
Provider defaults (tuned for free-tier daily quotas):
Gemini β 45% (4 RPM + budget=1 β ~150 calls/h β ~10h at 1500 RPD)
Groq β 70% (conserves daily token budget)
HF β 45%
Ollama/Claude β 100% (no quota)
Also reduces Gemini's _max_llm_calls_this_tick from 4 β 1 so the
rate limiter (15s/call) naturally paces the simulation without
needing to burn all 4 budget slots every tick.
Override default via SOCI_LLM_PROB env var (0.0β1.0).
API: GET /api/controls β llm_call_probability field
POST /api/controls/llm_probability?value=0.45
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix Gemini circuit-breaker: expire at midnight Pacific, not fixed 8hβ
The fixed 28800s (8h) window could block calls even after the daily
quota reset at midnight Pacific. Now computes exact seconds until
midnight Pacific so the circuit-breaker lifts the moment Gemini's
quota refreshes.
Also improves quota warning to show remaining requests and hours
until reset: "750 remaining, resets in 4.2h (midnight Pacific)".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix Gemini RPM (4 not 14) and add daily quota warnings at 50/70/90/99%β
Free tier is 5 RPM (not 15) β using max_rpm=14 caused every call to 429.
Now uses max_rpm=4 to stay safely under 5 RPM.
Adds _track_daily_request() called on every successful response:
- Warns at 50%, 70%, 90%, 99% of GEMINI_DAILY_LIMIT (default 1500 RPD)
- Resets counter at midnight Pacific Time
- Daily limit overridable via GEMINI_DAILY_LIMIT env var
Free tier facts: 5 RPM, ~1500 RPD, 250k TPM; resets midnight Pacific.
Cost on paid tier: ~$0.000133/request (~$0.20/day at 1500 RPD).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix Gemini quota circuit-breaker: detect 'quota' in 429 bodyβ
Gemini sends retry-after:5 even for daily quota exhaustion, so the
old 'wait > 30' check never fired. Now detect 'quota' in the body
and circuit-break for 8 hours, stopping the retry loop immediately.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix Gemini 429 log truncation: escape braces in response bodyβ
HF Space log system parses { as JSON object start, truncating the
error message. Escape { and } in all Gemini 429/error log lines so
the full response body is visible in logs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- HFInferenceClient: drop hf_soci_token / soci_token / HW_WR_TOKEN β
HF_TOKEN is the only supported var (auto-injected by HF Spaces)
- create_llm_client: same β only HF_TOKEN triggers HF auto-detection
- _choose_provider: show Groq + Gemini (both free-tier) before HF and Ollama;
remove has_claude (Claude never appeared in the menu anyway)
- HF now labelled "requires HF PRO / credits" to set correct expectations
- SOCI_PROVIDER / LLM_PROVIDER env var still overrides everything (can force
any provider including claude)
Tokens safe to delete from HF settings: soci_token, soci_read, hf_soci_token
SOCI_PROVIDER is not a secret β safe to use as a public Space variable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove paid providers from menu; circuit-break on 402 (no credits)β
- _choose_provider: offer only free providers (HF SmolLM3, Groq, Ollama);
Claude/Gemini can still be forced via SOCI_PROVIDER env var
- HFInferenceClient: add 402 to circuit-breaker (same as 401/403/410) so
the sim stops retrying when the token lacks Inference Providers permission
- Fix: add hf_soci_token Space secret with a personal HF token that has
Inference Providers (Write) permission to enable SmolLM3 for free
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix HF LLM: use free SmolLM3 as default, strip thinking tokensβ
The Qwen/7B model was causing 402 (credit balance depleted) because it
auto-routes to a paid third-party provider via the HF router.
SmolLM3-3B with :hf-inference runs on HF's own CPU cluster for free.
- Default model: HuggingFaceTB/SmolLM3-3B:hf-inference (no credits needed)
- Prepend /no_think to system prompts to disable SmolLM3 reasoning mode
- Strip <think>...</think> blocks from all HF responses as a safety net
- Add HW_WR_TOKEN to recognized env var names (Space write token)
- HW_WR_TOKEN / hf_soci_token now take priority over HF_TOKEN so a
personal token with credits overrides the Space's limited auto-token
- create_llm_client: detect HW_WR_TOKEN for HF auto-selection
- _choose_provider: detect HW_WR_TOKEN to offer HF in the menu
- Tested: complete() and complete_json() both work correctly with SmolLM3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix HF Inference: migrate to router.huggingface.co/v1, use Qwen as defaultβ
- Update HFInferenceClient base_url to https://router.huggingface.co/v1/
(old hf-inference/ path returns 404 since the API was restructured in 2025)
- Change POST path from models/{id}/v1/chat/completions to chat/completions
- Replace defunct Zephyr-7b default with Qwen/Qwen2.5-7B-Instruct
(auto-routed by the HF router, produces coherent responses)
- Add MODEL_HF_SMOL as a no-credits fallback (hf-inference CPU provider)
- _map_model: fall back to model itself rather than default_model for unknown IDs
- _choose_provider: detect HF_TOKEN/hf_soci_token/soci_token and offer HF
as the first option (before Groq) so HF Spaces auto-selects it
- create_llm_client: also detect hf_soci_token/soci_token for auto-selection
- Tested: complete() returns 'Hello there!', complete_json() returns correct JSON
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
api-inference.huggingface.co returned 410 Gone β HF shut it down.
New base URL: https://router.huggingface.co/hf-inference/
Endpoint: v1/chat/completions with model in request body.
Also circuit-break immediately on 410 (permanent error).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Silence JSON parse warnings for empty LLM responsesβ
When Groq/HF quota is exhausted complete() returns "" and the parser
was logging a warning for every empty string. Return {} immediately
for empty input instead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix HF serverless URL: use per-model endpoint pathβ
The generic /v1/chat/completions endpoint doesn't work for HF serverless.
The correct format is /models/{model_id}/v1/chat/completions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Read hf_soci_token env var for HF Inference API keyβ
Adds hf_soci_token to the fallback chain so the HF Space secret
created under that name is picked up automatically.
Lookup order: HF_TOKEN β hf_soci_token β soci_token
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix HF/Gemini LLM errors: token env var + rate limitingβ
- HFInferenceClient now reads soci_token as fallback to HF_TOKEN
- Set _last_error when no API key so test toast shows a useful message
- Include PROVIDER_GEMINI and PROVIDER_HF in is_rate_limited check so
the simulation loop caps LLM calls/tick (4 calls, 4s ticks) instead
of firing 10 concurrent requests and saturating the 15 RPM free tier
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix HF 503 cold-start: circuit-break immediately instead of blockingβ
Previously: waited 60s inline per attempt, exhausted retries, returned ""
with no error recorded β showed "may not be on serverless API".
Now: on first 503, read estimated_time from body, set _rate_limited_until,
record "503 model loading β retry in Xs" as _last_error, return "" so the
sim tick is not blocked. Model warms up during that window; next tick
after the timer expires will call it successfully.
Also: clear _last_error on success; set "all retries exhausted" fallback.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Show actual HTTP error in LLM test toast instead of 'empty response'β
Track _last_error on every HF failure path (non-auth errors).
Test endpoint returns error field combining auth_error and last_error.
Toast now shows model name + real error e.g. 'HTTP 404: Model not found'
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Switch HF default model to zephyr-7b-beta (ungated, no license needed)β
Qwen/Qwen2.5-7B-Instruct had no visible license gate for the user.
HuggingFaceH4/zephyr-7b-beta is fully open with no access restrictions.
HF_MODEL env var can still override to any other model.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add LLM call counter, test button, and better HF error handlingβ
- LLM pill now shows ΓN call count each tick in green when active
- LLM popup has a π¬ Test button β makes a real call and shows raw response
- GET /api/llm/test endpoint for diagnosing provider issues
- HF 401/403 triggers circuit breaker + nokey status (gated model / bad token)
- HF 503 reads estimated_time from response body and waits accordingly
- auth_error field exposed on /llm/test for actionable diagnostics
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Distinguish 'no token' from 'rate limited' in LLM status indicatorβ
HFInferenceClient returns 'nokey' status when HF_TOKEN is absent.
UI shows orange dot with tooltip 'no API token β set HF_TOKEN env var'
instead of red 'rate limit hit', making the cause immediately clear.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix HF provider switch: don't raise on missing HF_TOKENβ
Allow HFInferenceClient to initialize without a token so the provider
can be selected in the UI. Missing token logs a warning and calls
return empty string (same graceful degradation as Ollama without server).
llm_status reports "limited" when token is absent.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add Hugging Face Serverless Inference as free LLM providerβ
- HFInferenceClient uses OpenAI-compatible endpoint at api-inference.huggingface.co
- Default model: Qwen2.5-7B-Instruct (also supports Llama-3.2-3B, Mistral-7B)
- HF_TOKEN auto-injected in HF Spaces β zero config needed on Spaces deployment
- Circuit breaker on quota exhaustion (same pattern as Groq/Gemini)
- Auto-detected in create_llm_client() when HF_TOKEN is set (after Gemini, before Ollama)
- Provider switcher UI shows π€ icon for hf provider
- routes.py and server.py updated to accept "hf" as a valid provider
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Show live LLM status in header (active/limited/skipped/idle)β
Each LLM client now exposes an llm_status property:
- ClaudeClient: tracks _rate_limited_until on RateLimitError
- GroqClient / GeminiClient: reports circuit-breaker state
- OllamaClient: flags recent connection failures
get_state_summary() now includes llm_status, llm_calls_last_tick, and
llm_skipped so the WebSocket state carries everything the UI needs.
Header model pill gains a coloured dot:
green β calls are happening this tick
yellow β idle (no LLM calls needed)
orange/red β quota or rate limit hit
grey β LLM skipped (fast / 50x mode)
Tooltip shows provider, full model name, and a short status reason.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add render.yaml, fix gemini provider routing, show LLM model in headerβ
- render.yaml: declares LLM_PROVIDER=gemini as versioned config; secrets
(GEMINI_API_KEY, GROQ_API_KEY, etc.) listed as sync:false so they persist
from the Render dashboard without being stored in the repo
- server.py: _choose_provider() now accepts "gemini" from LLM_PROVIDER env var
- simulation.py: get_state_summary() includes llm_provider + llm_model fields
- index.html: header shows active model (e.g. β¦ 2.0-flash) with full name
as tooltip; icon varies by provider (β¦ gemini, β‘ groq, β claude, π¦ ollama)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add GeminiClient using OpenAI-compatible AI Studio endpoint (free tier:
15 RPM / 1M tokens/day on gemini-2.0-flash, set GEMINI_API_KEY to use)
- Auto-detect provider order: Claude β Groq β Gemini β Ollama
- Fix agent back-view: move direction tracking from drawPerson() to animate()
using pixel delta β reliable for all path angles, no waypoint guessing
- Add scripted fallback dialogue so conversations animate in the UI even
when no LLM is configured
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix apartment assignment, player sleep, gender profile, and NPC conversationsβ
- Player now gets assigned to an available apartment block instead of town_square
- Players auto-sleep at home during sleeping hours (23:00-06:00) and auto-wake
- Added gender field (male/female/nonbinary) to player profile modal and API
- Gender-based attraction: only opposite genders (or nonbinary/unknown) develop romance
- Players now included in romance system (_tick_romance no longer skips is_player)
- Fixed NPC conversations: replaced is_busy check with conversable-state check
so agents eating/relaxing/working can still initiate conversations
- Doubled conversation base chance (extraversion/20 instead of /40)
- Raised max simultaneous conversations cap from 2 to 3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reduce LLM call rate: shorter convos, less frequent, hard capβ
When Groq returns a 429 with retry-after > 15s (daily token quota
exhausted), a circuit breaker is armed and all subsequent LLM calls
return empty immediately until the quota window resets β instead of
blocking the simulation for 111+ seconds per call.
Short rate-limit waits (β€15s, per-minute limit) still wait and retry
as before. The simulation keeps running in routine/deterministic mode
while the quota is exhausted.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>