- botUserID no longer captured once at startup (was 'default' if no user yet)
- resolveBotUser() reads first registered user from DB on demand:
* called on every /start (handles: registered after bot launch)
* called before every AI message (handles mid-session registration)
- If no user registered: clear English error 'No account found. Please register on the web UI first'
- start.sh: fix set_env_var appending without newline (token was concatenated to prev line)
- sendMarkdownMsg() helper: sends with ParseMode=Markdown, falls back to plain text
- All formatted messages (langSelectionMsg, buildSetupGuide, helpMessage) now render
bold text and code blocks correctly in Telegram
- Simplify /start language check: isLangDefault(st) alone is sufficient
(lang == 'en' && isLangDefault was redundant — GetLanguage returns 'en' when empty)
store/telegram_config.go:
- Add Language field to TelegramConfig (persisted in DB)
- Add SetLanguage(lang) and GetLanguage() methods
- Default language: English (en)
telegram/bot.go:
- First /start triggers language selection (1=English, 2=中文)
- /lang command to change language at any time
- awaitingLang state machine handles language choice before any other input
- buildSetupGuide() now fully bilingual (EN/ZH), context-aware:
Step 1: configure AI model (no model yet)
Step 2: configure exchange (model OK, no exchange)
Ready: show full capabilities
- tryHandleSetupCommand() bilingual: 'configure/配置 <provider> <key>'
- helpMessage(lang) fully bilingual
- All error/status messages bilingual
Default: English. isLangDefault() detects whether user has explicitly
chosen a language vs falling back to the 'en' default.
start.sh:
- Interactive Telegram Bot Token prompt on first run
- Token format validation (must match 12345:ABC... pattern)
- Friendly step-by-step startup instructions after launch
telegram/bot.go:
- /start now shows context-aware setup guide based on actual config state:
- No AI model → explains how to configure, lists all providers
- AI model OK but no exchange → guides to configure exchange via chat
- All configured → full capabilities welcome message
- New: direct setup commands ('配置 deepseek sk-xxx') bypass LLM entirely
so AI model can be configured even before any model exists (bootstrap fix)
- All messages now in Chinese (匹配用户语言)
telegram/agent/prompt.go:
- Added first-time setup detection section
- Agent told to never ask user to visit web UI — everything via chat
GetAnyEnabled() searched across all users in DB — if user B has an
enabled model, bot could use their API key while acting as user A.
Now uses GetDefault(botUserID) which only looks up the bound user's
enabled model, matching the same user scope as all API calls.
Previously qwen/kimi/grok/gemini all fell back to DeepSeekClient.
Each provider now gets its own dedicated client with correct default
base URL and model. All 7 providers now fully supported:
openai, deepseek, claude, qwen, kimi, grok, gemini
- telegram/bot.go: clientForProvider now returns NewClaudeClient() for
'claude' provider (was incorrectly falling back to DeepSeekClient which
uses OpenAI wire format, breaking Anthropic API calls)
- api/server.go: fix scan_interval_minutes schema default (3, not 60);
POST /api/strategies now clearly states config is OPTIONAL with complete
working defaults; POST /api/traders removes redundant GET workflow note
- telegram/agent/prompt.go: simplify strategy creation — just POST {name}
without config (backend applies full working defaults automatically);
only include config when user requests custom settings
Migrate the Telegram bot agent from an XML tag hack (<api_call>) to
OpenAI-native function calling via CallWithRequestFull.
Key changes:
- mcp/interface.go: add parseMCPResponseFull to clientHooks interface
- mcp/client.go: route callWithRequestFull through hooks for overridability
- mcp/claude_client.go: override parseMCPResponseFull for Claude response
format (tool_use blocks instead of choices[].message.tool_calls)
- telegram/agent/agent.go: rewrite Run() to use CallWithRequestFull;
define api_request tool with JSON Schema; implement tool-call loop
with role="tool" result messages; remove XML parsing entirely
- telegram/agent/apicall.go: remove parseAPICall (dead code)
- telegram/agent/prompt.go: simplify — remove XML format instructions,
replace with concise api_request tool usage instructions
- telegram/agent/agent_test.go: rebuild all tests using LLMResponse
objects; add TestNarrationStructurallyImpossible, TestOnChunkCalledWithFinalReply,
TestToolCallIDPropagated; remove XML-specific tests
Architecture advantage: with native function calling, the LLM returns
EITHER ToolCalls OR Content — never both. Narration is now structurally
impossible at the protocol level, not just enforced by prompt rules.
All 11 agent tests pass. mcp package tests pass.
- Rewrite NO NARRATION rule: response is EITHER api_call tag alone OR
final text reply — no text before api_call under any circumstances
- Ban all narration patterns: 现在我将/好的/正在/I will/Let me etc.
- Add 'create strategy + create trader + start' full setup workflow
- Add 12 automated tests covering:
- No narration leaking to user (5 narration variants tested)
- api_call tag never leaks to user
- Full setup workflow: POST strategy → verify → POST trader → start
- Start existing trader workflow
- Max iterations safety, tag stripping, parser edge cases
- Add Telegram bot with long-polling and AI agent loop (api_call tool)
- SSE streaming with real-time message editing and ⏳ placeholder
- Account state injection at conversation start (models, exchanges,
strategies, traders, per-trader PnL and statistics)
- Lane semaphore per chat serializes concurrent messages (60s timeout)
- Idle timeout watchdog (60s) prevents hung streaming connections
- Look-ahead buffer prevents partial <api_call> tag leaking to user
- Fix PUT /strategies/:id to merge config (read-then-merge pattern)
- Add route registry with full API schema for LLM documentation
- Add TelegramConfig store and Web UI config modal
- Add GetAnyEnabled to AIModel store for bot LLM client selection