Commit Graph

309 Commits

Author SHA1 Message Date
Kunthawat Greethong
e572bc910f fix: boolean server_default must be 'false' not '0' for Postgres
PostgreSQL rejects BOOLEAN DEFAULT 0 (DatatypeMismatch: column ... is of type
boolean but default expression is of type integer). Migration 0008
(platform_settings.active) crashed the alembic upgrade run by the entrypoint,
which in turn crash-looped the worker. Migration 0011 (password_reset_tokens.
used) had the same latent bug and would have failed next.

Change the Boolean server_default from text('0') to text('false') in both
migrations and both ORM models so the DDL is valid on both PostgreSQL
(production) and SQLite (local/tests).

Verified: full 0001->0011 chain runs on fresh SQLite; alembic check reports no
drift; backend suite 201 passed.
2026-08-31 22:21:53 +07:00
Kunthawat Greethong
851ed65f45 build: resolve torch as CPU-only to eliminate CUDA download
camel-oasis (transitively via sentence-transformers) hard-imports torch in
oasis/social_platform/recsys.py, so torch cannot be removed while OASIS
simulation is a feature. On linux-x86_64 the default PyPI torch wheel is the
CUDA build, which dragged ~several GB of nvidia-* packages into the image
even though this server has no GPU and all LLM + embedding calls go through
an API (generate_post_vector_openai).

Fix: force torch to resolve from PyTorch's CPU-only index via [tool.uv]:
- override-dependencies torch==2.13.0, [[tool.uv.index]] pytorch-cpu, and
  [tool.uv.sources] torch={index=pytorch-cpu}.

Result after re-lock: all nvidia-* + triton packages removed (0 remaining in
uv.lock), torch 2.9.1 -> 2.13.0+cpu. Verified: torch/sentence_transformers/
oasis/from app import create_app all import fine with CPU torch
(cuda: False); backend suite 201 passed. Dockerfile keeps an import-time
verify after uv sync instead of the now-unneeded nvidia uninstall step.
2026-08-31 21:11:32 +07:00
Kunthawat Greethong
2fe8482ad4 build: strip NVIDIA CUDA runtime libs from image (no local GPU)
camel-oasis (dep of camel-ai/sentence-transformers) pulls in torch on
linux-x86_64, which drags several GB of nvidia-cuda-* / cudnn / triton
packages into the production image even though this deployment never runs an
LLM locally — all LLM calls go through an API (OpenAI-compatible) and the
server has no GPU. The nvidia-* packages are pure bloat.

After 'uv sync', uninstall all nvidia-* runtime libs + triton (torch itself
stays as a CPU runtime). Then verify the stripped env still imports psycopg,
torch, sentence-transformers and the app, so the build fails loudly if the
strip breaks anything instead of failing silently at container runtime.

Verified locally (no CUDA libs present): psycopg/torch/sentence_transformers/
create_app all import fine.
2026-08-31 20:38:03 +07:00
Kunthawat Greethong
fb9275818e fix: normalize bare postgres:// to postgresql+psycopg:// (worker crash-loop)
Root cause (confirmed on local): even with psycopg installed, SQLAlchemy
raises:
  NoSuchModuleError: Can't load plugin: sqlalchemy.dialects:postgres
when DATABASE_URL uses the bare 'postgres://' scheme, because SQLAlchemy
only resolves 'postgresql+driver://'. The deploy's DATABASE_URL was
'postgres://...', so alembic upgrade head (run by the entrypoint before
starting services) crashed and the worker crash-looped in supervisor.

Fix:
- create_database_engine now normalizes 'postgres://' and legacy
  'postgres+pq://' to 'postgresql+psycopg://' so a bare postgres scheme
  works as long as psycopg is installed.
- Dockerfile build step now verifies psycopg imports after 'uv sync'
  (fails the build loudly instead of a runtime crash-loop).
- Tests: 4 for URL normalization; backend suite now 201 passed.
2026-08-31 20:13:16 +07:00
Kunthawat Greethong
8953b8f066 fix: ensure Postgres driver installs in image & stop uv re-sync at runtime
Worker crash-loop root cause (from container log):
  sqlalchemy.exc.NoSuchModuleError: Can't load plugin: sqlalchemy.dialects:postgres

Two compounding issues:
1. Dockerfile copied pyproject.toml/uv.lock, ran 'uv sync --frozen',
   then 'COPY backend ./backend' which OVERWROTE those dep files with the
   shipped versions. The two could differ, so every entrypoint 'uv run'
   detected drift and REBUILT/re-synced the project at container runtime
   (seen as repeated 'Building crowdsight-backend...' + 'Uninstalled N /
   Installed 1'), never installing the psycopg Postgres driver that the
   image's own lock actually lists.
2. Result: alembic upgrade head over a postgres DATABASE_URL crashed with
   NoSuchModuleError -> worker crash-loop.

Fix:
- Dockerfile: COPY backend (full source) BEFORE 'uv sync --frozen --no-dev',
  so the installed deps match the shipped pyproject.toml/uv.lock exactly.
- Use 'uv run --frozen' for alembic/gunicorn/worker so nothing re-syncs at
  runtime.
- entrypoint: fail fast with a clear message if DATABASE_URL is postgres
  but psycopg is missing (instead of a confusing alembic traceback).
Verified: entrypoint bash syntax ok; 'uv run --frozen ... import psycopg'
passes; psycopg present in git-tracked uv.lock + pyproject.
2026-08-31 18:56:18 +07:00
Kunthawat Greethong
42208c4f5a fix: run alembic migrations before starting services
Production image had no DB migration step, so a fresh container had an
empty database: the durable worker queried the 'jobs' table before it
existed and crash-looped with sqlalchemy OperationalError 'no such table:
jobs' (supervisor restart loop).

- Add backend/docker_entrypoint.sh: fail-fast if DATABASE_URL is unset,
  run 'alembic upgrade head' (idempotent), then exec supervisord.
- Dockerfile CMD now runs the entrypoint.
- supervisor: fix nodaemon typo, stream stdout/stderr to /dev/stdout +
  /dev/stderr so worker errors are visible in container logs, and give
  worker startsecs/startretries.

Verified locally: entrypoint bash syntax ok, alembic upgrade head
idempotent, jobs + 19 tables created, worker --once exits 0 after migrate
(previously exit 1 with no-such-table). Worker/schema tests 11 passed.
2026-08-31 15:16:59 +07:00
Kunthawat Greethong
8b84378fe1 feat: SaaS foundation for CrowdSight
Elevate MiroFish/CrowdSight from single-container dev to a SaaS foundation:

- Local memory backend (Zep-compatible): memory services/models, local graph
  builder + updater, AgentActivity seam, import-boundary isolation; Zep stays
  default, local is opt-in behind MEMORY_BACKEND. Semantic parity not yet proven.
- Durable product persistence: projects/simulations/reports schema (migration
  0007) + tenant/owner-scoped ProductRepository + dual-write + scoped_project
  read-first + ArtifactStore abstraction; durable JobQueue + worker.py.
- SaaS hardening: durable RateLimiter (wired to login), UsageService (LLM
  accounting), redacted AuditService, idempotency, CORS allowlist, safe API
  errors, single-use PasswordResetService + endpoints (covers invite-pending).
- Exactly 3 roles (super_admin/admin/user) with tenant authz policy.
- Admin UI: GET/POST/PATCH /api/admin/users + GET/PUT /api/admin/settings
  (super-admin only, encrypted/masked); AdminView.vue + SettingsView.vue with
  admin/super-admin route guards, th/en i18n.
- Production deploy topology: multi-stage Dockerfile (frontend build + gunicorn
  wsgi + nginx SPA-proxy + supervisord worker), backend/wsgi.py, gunicorn dep.

Backend 197 passed; frontend 10 tests + build green. ruff unavailable (gap).
No commit of credentials; secrets handled via env/.env.example.
Deferred: Zep semantic A/B parity, object storage cutover, mobile QA, EasyPanel
container build of deploy topology.
2026-08-31 13:05:21 +07:00
Kunthawat Greethong
89d04e795b fix: replace hero logo with inline use cases grid
- Remove logo image from hero section
- Move use cases into hero-right as 2x2 grid
- Remove standalone usecases-section
- Update CSS for inline layout
2026-06-27 13:11:29 +07:00
Kunthawat Greethong
da5a32d014 fix: add missing json import in simulation.py 2026-06-26 20:58:26 +07:00
Kunthawat Greethong
3fc96f39dd fix: wire agent group selection through to simulation start
Step 2 -> Step 3 -> Simulation API now passes selected_agent_ids.
Backend filters reddit_profiles.json and twitter_profiles.csv
to only include selected agents before starting simulation.

Flow: Step2 checkboxes -> emit('next-step', {selectedAgentIds}) ->
router query -> Step3 props -> startSimulation API -> filter profiles
2026-06-26 13:48:23 +07:00
Kunthawat Greethong
b7bc09d650 fix: show categorize button BEFORE groups exist
The 'จัดกลุ่มอัตโนมัติ' button was hidden inside v-if='agentGroups.length > 0'
— chicken-and-egg problem. Now shows trigger button when profiles exist
but groups don't, and groups section after categorization.
2026-06-26 13:14:16 +07:00
Kunthawat Greethong
d41d865313 Phase 5: Agent grouping and selection after Step 2
Backend:
- Add /api/agent-group/categorize endpoint — AI groups agents by role
- Add /api/agent-group/filter endpoint — filter by selected groups
- Groups with default_enabled=false (advertiser, brand) are unchecked

Frontend:
- Add agent groups section in Step2EnvSetup.vue
- 'Auto-categorize' button triggers AI grouping
- Show groups with checkboxes (enabled groups checked, disabled unchecked)
- Auto-remove unchecked agents when proceeding to Step 3
- Show selected count summary
2026-06-26 12:36:03 +07:00
Kunthawat Greethong
dd3db561e5 fix: strengthen entity filtering for ad/business content
- Add marketing metadata to 'Not allowed' list in ontology prompt
- Strengthen exclude_self filter instruction
- Add exclude_rules support from template filter rules
- Update business_ad template with more excluded types
2026-06-26 12:19:45 +07:00
Kunthawat Greethong
dec6bda349 fix: update uv.lock after renaming to crowdsight-backend 2026-06-26 11:59:50 +07:00
Kunthawat Greethong
d5cc005332 fix: revert logo filename reference (file not renamed yet) 2026-06-26 11:47:34 +07:00
Kunthawat Greethong
c9f76babeb Phase 4.4: Context-aware entity filtering in Step 1
- OntologyGenerator.generate() now accepts template_filter_rules parameter
- When template_id is provided, API loads filter rules from templates.json
- Filter rules injected into ontology system prompt:
  - exclude_self: don't create entity for the business/brand that uploaded data
  - exclude_types: don't create specific entity types
  - focus: guide LLM to focus on specific entity categories
- API endpoint accepts template_id in form data
2026-06-26 11:46:37 +07:00
Kunthawat Greethong
166ef73ad2 Phase 4: Template system with auto-select and pre-fill
Backend:
- Add templates.json with 5 template definitions (news, policy, business, fiction, social)
- Add template API (/api/template/list, /api/template/auto-select, /api/template/:id/filter-rules)
- Register template blueprint in Flask app

Frontend:
- Add template API client (frontend/src/api/template.js)
- Add template selector UI in Home.vue (chip buttons + auto-select button)
- Add template state management and auto-select logic

Locale:
- Add template keys for th/en/zh

Entity filter rules in templates.json for context-aware filtering in Step 1.
2026-06-26 11:44:55 +07:00
Kunthawat Greethong
3c4c2183c7 Phase 3: Redesign homepage + natural Thai text
- Add use cases section with 4 scenario cards (news, policy, business, fiction)
- Rewrite Thai home section text to be natural (not translated from Chinese)
- Add use case locale keys for th/en/zh
- Add CSS for use cases grid with hover effects
2026-06-26 11:41:13 +07:00
Kunthawat Greethong
596a75c229 Phase 1+2: Rename CrowdSight + fix Thai vocabulary
Phase 1: Rename MiroFish → CrowdSight across all files
- 39 files, 114+ occurrences replaced
- Frontend, backend, locales, config, README, docker-compose

Phase 2: Fix difficult Thai vocabulary
- เมล็ดพันธุ์แห่งความจริง → ข้อมูลตั้งต้น
- สกัดเอนทิตี → ดึงตัวละคร
- ฉีดความจำ → เพิ่มความจำ
- ออนโทโลยี → โครงสร้างข้อมูล
- เอนทิตี → ตัวละคร
- พลวัตกลุ่ม → พฤติกรรมกลุ่ม
- โลกคู่ขนาน → โลกจำลอง

Only string changes, no logic changes.
2026-06-26 10:27:48 +07:00
Kunthawat Greethong
0e263f0490 fix: Step 5 — camel-ai reads OPENAI_API_BASE_URL not OPENAI_BASE_URL
Root cause found in container: camel-ai v0.2.78 openai_model.py L117 reads
os.environ.get('OPENAI_API_BASE_URL') — NOT OPENAI_BASE_URL.

Fix: Set BOTH env vars (OPENAI_BASE_URL for OpenAI SDK + OPENAI_API_BASE_URL for camel-ai).
Keep model_config_dict={} empty so nothing spreads to create().

Also fix Step 2 Thai truncation: \w regex doesn't match Thai tone marks (Mn category).
Use explicit Unicode range \u0E00-\u0E7F instead.
2026-06-22 11:42:56 +07:00
Kunthawat Greethong
afc7afa2f5 fix: 3 fixes for Step 2, Step 4, Step 5
1. Step 5: Use empty model_config_dict={} so camel-ai doesn't spread
   api_key into create() - AsyncOpenAI reads env vars automatically.
   Step 3 unaffected (same env vars, just cleaner ModelFactory call).

2. Step 2: Fix Thai text truncation - isalnum() stripped Thai chars.
   Use re.sub(r'[^\w]', '', username, re.UNICODE) instead.

3. Step 4: Move get_language_instruction() to START of prompt (not end)
   and strengthen wording with MUST/IMPORTANT prefix.
2026-06-22 10:57:57 +07:00
Kunthawat Greethong
270c92ed05 fix: 3 fixes - camel-ai env var, Thai font, model_config_dict
1. Restore OPENAI_API_KEY/OPENAI_BASE_URL env vars for camel-ai factory check
   (keep api_key/base_url in model_config_dict for client constructor)
2. Add Thai-supporting font-family to .profile-realname
   (JetBrains Mono doesn't render Thai diacritics)
3. Keep model_config_dict with api_key and base_url for camel-ai client
2026-06-22 10:20:44 +07:00
Kunthawat Greethong
cfaa6e8b8d fix: pass api_key/base_url via model_config_dict, not env vars
camel-ai v0.2.78 reads OPENAI_API_KEY from env and auto-passes it to
chat.completions.create() which doesn't accept it (TypeError).

Fix: pass api_key and base_url through model_config_dict so camel-ai
extracts them for the OpenAI client constructor only.
2026-06-18 20:12:51 +07:00
Kunthawat Greethong
3ba42db6e2 fix: revert to env var approach for camel-ai LLM config
model_config_dict passes values to create() call, not client constructor.
api_key in model_config_dict causes 'unexpected keyword argument' error.
Revert to env vars: OPENAI_API_KEY + OPENAI_BASE_URL (camel-ai reads both).
2026-06-18 10:33:10 +07:00
Kunthawat Greethong
31d1ebd49b fix: pass model_type as positional arg to camel-ai ModelFactory.create
model_type must be a separate argument, not inside model_config_dict.
Also ensure all 3 scripts have consistent ModelFactory.create calls.
2026-06-18 09:17:34 +07:00
Kunthawat Greethong
b30ed19b16 fix: pass base_url and api_key directly to camel-ai ModelFactory
camel-ai does not read OPENAI_BASE_URL env var reliably.
Pass api_key and base_url via model_config_dict instead.
2026-06-17 22:30:59 +07:00
Kunthawat Greethong
7c3e219a6f fix: CSS syntax error in survey-env-warning placement 2026-06-17 21:58:00 +07:00
Kunthawat Greethong
7f04bc44fb fix: use correct env var OPENAI_BASE_URL for camel-ai LLM routing
camel-ai's OpenAI model reads OPENAI_BASE_URL, not OPENAI_API_BASE_URL.
This caused all simulation LLM calls to go to api.openai.com instead of
the configured provider (DeepSeek, Xiaomi Mimo, etc), resulting in 401.
2026-06-17 21:46:45 +07:00
Kunthawat Greethong
2bb1afab68 fix: extract actual error message from backend error responses
The error interceptor was re-throwing the generic axios Error object
instead of extracting the actual error message from the response body.
Now extracts error.response.data.error for meaningful error messages.
2026-06-17 21:40:21 +07:00
Kunthawat Greethong
5224c6df79 fix: show actual error details in chat error messages
- Extract error from err.response.data.error (axios error response)
- Log full error to console for debugging
- Show actual backend error message instead of generic 'Error'
2026-06-17 21:38:19 +07:00
Kunthawat Greethong
55a430db62 fix: show env status warning in survey tab + add alert on survey failure
- Add isEnvAlive check on mount via /api/simulation/env-status
- Show warning banner when simulation env is not running
- Add alert() on survey failure for visibility
- Add envNotRunning translation key for th/en/zh
2026-06-17 21:35:08 +07:00
Kunthawat Greethong
fa3160f67e fix: show visible error alert when survey fails (e.g. simulation env not running) 2026-06-17 19:37:55 +07:00
Kunthawat Greethong
431b66fd85 fix: translate remaining Chinese in sim config and profile prompts
- Time config: translate all Chinese instructions and field descriptions
- Event config: translate hot topics/narrative direction instructions
- Agent config: translate entity type descriptions and field labels
- Profile generator: translate all persona prompt fields and instructions
- Country field: changed from 'use Chinese' to 'use English'
2026-06-17 19:36:05 +07:00
Kunthawat Greethong
5fcce79361 fix: translate Chinese prompts to English in simulation config and profile generators
- simulation_config_generator.py: translate all LLM prompts and system messages
- oasis_profile_generator.py: translate profile generation prompts

Ensures get_language_instruction() controls output language instead of
being overridden by Chinese prompt context.
2026-06-17 15:52:38 +07:00
Kunthawat Greethong
bf14c56944 fix: translate ONTOLOGY_SYSTEM_PROMPT from Chinese to English 2026-06-17 15:32:47 +07:00
Kunthawat Greethong
e766bc625a fix: translate all Chinese prompts to English in zep_tools.py
- Interview prompt prefix: Chinese -> English
- Sub-query decomposition: Chinese -> English
- Agent selection: Chinese -> English
- Interview questions: Chinese -> English
- Interview summary: Chinese -> English
- Error messages: Chinese -> English
- to_text() labels: Chinese -> English

This ensures get_language_instruction() actually controls output language
instead of being overridden by Chinese prompt context.
2026-06-17 15:28:00 +07:00
Kunthawat Greethong
8a09a49029 feat: add Thai language support (100% Thai UI)
- Add locales/th.json with 629 translated keys
- Add Thai to languages.json with llmInstruction for report generation
- Backend auto-loads new locale files from locales/ directory
2026-06-17 12:38:33 +07:00
Kunthawat Greethong
9f2232d40b feat: support cross-origin deployment with VITE_API_BASE_URL
- Set VITE_API_BASE_URL=https://opinion-api.moreminimore.com for
  frontend-backend split deployment
- Vite proxy also uses API_BACKEND_URL env var as target
- Falls back to same-origin proxy for local dev
2026-06-17 12:11:22 +07:00
Kunthawat Greethong
aadc11ad83 fix: use relative baseURL so API calls go through vite proxy
When deployed behind a reverse proxy (e.g. opinion.moreminimore.com),
hardcoding http://localhost:5001 causes the browser to try connecting
to the user's own machine. Empty baseURL + vite proxy fixes this.
2026-06-17 12:08:04 +07:00
Kunthawat Greethong
cbe861ff27 fix: use allowedHosts=true (boolean), disable open in container 2026-06-17 11:57:24 +07:00
Kunthawat Greethong
26cdd14881 fix: use VITE_ALLOWED_HOSTS env var, default to 'all' 2026-06-17 11:55:56 +07:00
Kunthawat Greethong
0cdcc79125 fix: allow all hosts in vite dev server 2026-06-17 11:47:38 +07:00
Kunthawat Greethong
3ea51d906a fix: allow opinion.moreminimore.com host in vite config 2026-06-17 11:46:59 +07:00
Kunthawat Greethong
f395309207 feat: add DeepSeek and Xiaomi MiMo LLM provider presets
- Add providers.py with 5 provider presets (OpenAI, DeepSeek, Xiaomi MiMo, Alibaba DashScope, MiniMax)
- Add LLM_PROVIDER env var for one-line provider switching
- Improve <think> tag stripping for reasoning models
- Add .env.example with documented configuration
- Update README with provider configuration section
2026-06-17 11:13:34 +07:00
BaiFu
96096ea0ff Merge pull request #640 from lllopic/fix/add-type-hints-and-helper-method
refactor: add type hints and FileParser.is_supported() helper
2026-05-25 00:48:57 +08:00
666ghj
3f4d56116c fix(backend): constrain Python version to 3.11-3.12 2026-05-24 22:59:36 +08:00
BaiFu
db1bc144ff Merge pull request #641 from YunyueLi/docs/readme-polish
docs(readme): fix typo in Shanda logo alt text
2026-05-24 02:02:46 +08:00
YunyueLi
faa151131c docs(readme): fix typo in Shanda logo alt text
The Shanda sponsor logo's alt text was `666ghj%2MiroFish | Shanda`,
missing the `F` from the URL-encoded `/`. Every other badge in both
READMEs uses the correct `666ghj%2FMiroFish`. Bring this one in line
with the rest.
2026-05-23 16:30:55 +08:00
lllopic
daec4b6be4 refactor: add type hints and FileParser.is_supported() helper
- Add return type annotation (list[str]) to Config.validate()
- Add type annotations (msg: str, -> None) to logger convenience functions
- Add FileParser.is_supported() classmethod for checking file format support
2026-05-23 14:57:46 +08:00
666ghj
fa0f6519b1 docs: rename README-EN.md to README.md as default English documentation 2026-04-02 16:52:29 +08:00