feat(fallback): unelte, internet, retea si conversatie pe modelul local
Fallback-ul local (Qwen3.5-2B, LXC 104) raspundea ca nu are unelte sau
internet. Acum conversează, are unelte read-only si istoric per canal.
Unelte (allowlist strict read-only, src/local_fallback_tools.py):
- cauta_web — DuckDuckGo Lite, fara API key
- masini — status Proxmox/LXC prin SSH paralel (src/net_status.py)
- vremea, sold, facturi, trezorerie, doctor, logs, email, kb,
cauta_memorie, citeste_pagina
Protocol: function calling OpenAI nativ. Protocolul text anterior
("TOOL: nume arg") pierdea argumentul in 100% din cazuri.
Uneltele cu output deja formatat se intorc verbatim — modelul de 2B
transforma un `doctor` cu 5 linii OK in "Sistemul este in 5/5 state.".
Doua treceri: prima decide uneltele (cu tools, temp 0), a doua reface
turnul fara ele — prezenta definitiilor strica raspunsurile simple
("cat fac 128/4?" -> "Nu stiu ce inseamna 128/4"). Few-shot doar pe
cereri creative: pe turnuri factuale strica aritmetica (17*23 -> 471).
Ocoliri deterministe pentru ce modelul greseste reproductibil: vremea si
preturile live (raspundea din memorie), plus sarcini "instructiune: text"
(payload-ul dicta unealta — "scrie mai politicos: da-mi raportul acum"
chema `sold`). llama.cpp nu e determinist nici la temperatura 0 si
ignora tool_choice, deci promptul singur nu ajunge.
Masurat: 36/36 pe set combinat (17 alegere unealta + 19 comprehensiune),
de la 14/19 pe comprehensiune inainte de regulile negative din prompt.
Comenzi: /f (inlocuieste /testfallback, ramas alias), /f reset, /masini.
Prefixul "Claude e la limita" apare doar pe calea de rate limit.
roa2web: /otp <cod> pentru 2FA din chat — mesajul de eroare trimitea la
verify_2fa(code, email), imposibil de rulat din Discord.
Corectat memory/kb/tools/infrastructure.md: LXC 101 si 110 sunt pe pve1.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B88u3KkzvUNnzU56y7GBn2
This commit is contained in:
@@ -15,7 +15,7 @@ from src.claude_session import (
|
||||
PROJECT_ROOT,
|
||||
VALID_MODELS,
|
||||
)
|
||||
from src.fast_commands import dispatch as fast_dispatch, split_text_chunks, extract_url_text
|
||||
from src.fast_commands import dispatch as fast_dispatch, split_text_chunks, extract_url_text, set_channel_context
|
||||
from src.router import (
|
||||
route_message,
|
||||
_ralph_propose,
|
||||
@@ -676,6 +676,35 @@ def create_bot(config: Config) -> discord.Client:
|
||||
f"Heartbeat error: {e}", ephemeral=True
|
||||
)
|
||||
|
||||
@tree.command(name="otp", description="Cod OTP pentru re-autentificare roa2web")
|
||||
@app_commands.describe(cod="Codul primit pe email", email="Doar prima dată")
|
||||
async def otp_cmd(
|
||||
interaction: discord.Interaction, cod: str, email: str | None = None
|
||||
) -> None:
|
||||
# Ephemeral throughout: the code is a live credential until it is used.
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
args = [cod] + ([email] if email else [])
|
||||
result = await asyncio.to_thread(fast_dispatch, "otp", args)
|
||||
await interaction.followup.send(result, ephemeral=True)
|
||||
|
||||
@tree.command(name="f", description="Vorbește cu modelul local de fallback (Qwen3.5-2B)")
|
||||
@app_commands.describe(text="Mesajul tău; 'reset' golește istoricul")
|
||||
async def f_cmd(
|
||||
interaction: discord.Interaction, text: str | None = None
|
||||
) -> None:
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
# The fallback runs tools over SSH/HTTP and can take tens of seconds;
|
||||
# set_channel_context is thread-local, so it must run in the same
|
||||
# worker thread as the dispatch itself.
|
||||
channel_id = str(interaction.channel_id)
|
||||
|
||||
def _run() -> str:
|
||||
set_channel_context(channel_id)
|
||||
return fast_dispatch("f", text.split() if text else [])
|
||||
|
||||
result = await asyncio.to_thread(_run)
|
||||
await interaction.followup.send(result[:1990], ephemeral=True)
|
||||
|
||||
@tree.command(name="search", description="Search Echo's memory")
|
||||
@app_commands.describe(query="What to search for")
|
||||
async def search_cmd(
|
||||
|
||||
Reference in New Issue
Block a user