Commit Graph

116 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
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
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
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
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
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
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
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
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
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
BaiFu
af71244974 Merge pull request #428 from Ghostubborn/feat/i18n
feat(i18n): 添加多语言切换功能,支持中英文
2026-04-02 14:27:04 +08:00
ghostubborn
f2404903d6 fix(i18n): validate Accept-Language header against registered locales 2026-04-02 14:20:15 +08:00
ghostubborn
24e9bee5be feat(i18n): replace all user-visible Chinese logger messages in zep_tools.py
These are shown to users via ConsoleLogger in the report page.
2026-04-01 17:46:39 +08:00
ghostubborn
e79569ab4f feat(i18n): replace all user-visible Chinese in report_agent.py
Covers ReportLogger message fields and logger messages shown via ConsoleLogger.
2026-04-01 17:44:52 +08:00
666ghj
e3350a919d fix(graph): enforce PascalCase for entity names and SCREAMING_SNAKE_CASE for edge names in ontology validation 2026-04-01 17:42:27 +08:00
ghostubborn
380e456d41 fix(i18n): replace hardcoded Chinese stage names in simulation prepare SSE 2026-04-01 17:31:00 +08:00
ghostubborn
0e55e4cf6b feat(i18n): replace remaining Chinese in config generator and profile generator
Also update simulation prompts to be locale-neutral for timezone/schedule.
2026-04-01 17:19:12 +08:00
ghostubborn
7c07237544 fix(i18n): pass locale to background threads via thread-local storage
Background threads (graph building, simulation prep, report generation,
profile generation) now inherit the requesting user's locale preference.
Previously these fell back to 'zh' because Flask request context was
unavailable in spawned threads.
2026-04-01 16:55:51 +08:00
ghostubborn
592ee52f59 feat(i18n): replace remaining hardcoded Chinese in progress callbacks 2026-04-01 16:53:29 +08:00
ghostubborn
da2490ec31 fix(i18n): protect JSON field values from language instruction in config generator
Ensure poster_type stays PascalCase English and stance stays English enum
values regardless of language setting. Only natural language fields follow
the user's language preference.
2026-04-01 16:41:22 +08:00
ghostubborn
97aa58384e fix(i18n): ensure ontology names stay PascalCase regardless of language setting
The language instruction was causing LLM to change entity/relation naming
conventions. Now explicitly enforce PascalCase/UPPER_SNAKE_CASE for technical
identifiers while only applying language preference to description fields.
2026-04-01 16:40:17 +08:00
ghostubborn
9d43b77511 feat(i18n): replace hardcoded Chinese in backend SSE progress messages 2026-04-01 16:32:10 +08:00
ghostubborn
f75c6487b3 fix(i18n): replace remaining hardcoded language directives in LLM prompts
- oasis_profile_generator: replace hardcoded "使用中文" with dynamic get_language_instruction()
- ontology_generator: remove hardcoded "(中文)" from schema annotation
- report_agent: replace Chinese-specific language consistency rules with language-neutral ones
- zep_tools: dynamically select quote style based on locale
2026-04-01 15:55:04 +08:00
ghostubborn
74f673a238 feat(i18n): replace hardcoded Chinese in backend API responses with t() calls 2026-04-01 15:32:24 +08:00
ghostubborn
8f6110df0f feat(i18n): inject language instruction into LLM system prompts 2026-04-01 15:24:12 +08:00
ghostubborn
0c18e1aeca feat(i18n): add backend translation utility with shared locale files 2026-04-01 15:22:14 +08:00
666ghj
985f89f49a fix: resolve 500 error caused by <think> tags and markdown code fences in content field from reasoning models like MiniMax/GLM 2026-03-06 00:30:31 +08:00
666ghj
da6548e96f feat(graph): implement pagination for fetching nodes and edges; add utility functions for streamlined data retrieval 2026-02-27 15:53:29 +08:00
666ghj
25aa4f75d2 fix(report_agent): refine tool call handling and response validation; enforce strict separation between tool calls and final answers 2026-02-24 17:47:44 +08:00
666ghj
08ec856a58 fix(report_agent): update max_agents parameter description and enforce maximum limit of 10 agents 2026-02-14 18:35:05 +08:00