05-sep: [feat] streaming en bot OpenCode (prompt_async + entrega incremental como el terminal)

This commit is contained in:
juanjo
2026-09-05 22:55:47 +02:00
parent 23fe44aecd
commit e00aa9294d

View File

@@ -376,6 +376,8 @@ def ok_sesion(chat):
def procesar_texto(chat, texto):
"""Envia la peticion de forma asincrona y entrega el resultado por partes
(streaming), para que el usuario vea progreso como en el terminal."""
sid, err = ok_sesion(chat)
if not sid:
enviar(chat, f"❌ No hay sesión OpenCode disponible: {err}")
@@ -384,15 +386,68 @@ def procesar_texto(chat, texto):
modelo = st.get("model")
stop = typing(chat)
t0 = int(time.time() * 1000)
try:
data, err = enviar_mensaje(sid, texto, modelo=modelo)
finally:
payload = {"parts": [{"type": "text", "text": texto}]}
if modelo:
payload["model"] = {
"providerID": modelo.get("providerID"),
"modelID": modelo.get("id"),
}
code, resp = oc_post(f"/session/{sid}/prompt_async", payload, timeout=40)
if code not in (200, 204):
stop.set()
if data is None:
enviar(chat, f"❌ Error del servidor OpenCode: {err}")
enviar(chat, f"❌ Error al lanzar OpenCode: {resp}")
return
rtexto, _ = extraer_respuesta(data)
# ficheros tocados en este turno
vistos_texto = set()
vistos_tool = set()
comienzo = time.time()
terminado = False
while time.time() - comienzo < 900:
time.sleep(3)
code, hist = oc_get(f"/session/{sid}/message?limit=4")
if code == 200 and isinstance(hist, list):
for item in reversed(hist[-4:]):
info = item.get("info") or {}
if info.get("role") != "assistant":
continue
for p in item.get("parts") or []:
t = p.get("type")
pid = p.get("id") or ""
if t == "text" and p.get("text") and pid not in vistos_texto:
vistos_texto.add(pid)
enviar(chat, formatear_html(p["text"]))
elif t == "tool" and pid not in vistos_tool:
vistos_tool.add(pid)
comando = ((p.get("state") or {}).get("input")
or {}).get("command")
if comando:
corto = comando.strip().replace("\n", " ")[:90]
try:
import requests as _rq
_rq.post(f"{API}/sendMessage",
data={"chat_id": chat, "text":
f"⚙️ <i>{html.escape(corto)}</i>",
"parse_mode": "HTML"},
timeout=10)
except Exception:
pass
# fin del turno: la sesion deja de estar busy (tras un margen inicial)
code2, sts = oc_get("/session/status", timeout=10)
transcurrido = time.time() - comienzo
if (code2 == 200 and isinstance(sts, dict)
and sts.get(sid, {}).get("type") != "busy"
and transcurrido >= 5):
terminado = True
break
stop.set()
if not terminado:
enviar(chat, "⏰ La tarea está tardando demasiado. Usa <code>/salir</code> "
"para cortarla y empezar de nuevo.")
return
# ficheros tocados en el turno
adjuntos = []
try:
code, hist = oc_get(f"/session/{sid}/message?limit=8")
@@ -401,14 +456,10 @@ def procesar_texto(chat, texto):
if os.path.isfile(r)]
except Exception as e:
log(f"adjuntos error: {e}")
if rtexto:
enviar(chat, formatear_html(rtexto))
if adjuntos:
enviar(chat, f"📎 {len(adjuntos)} fichero(s) del turno:")
for ruta in adjuntos[:6]:
enviar_doc(chat, ruta)
if not rtexto and not adjuntos:
enviar(chat, "🤔 (sin respuesta de texto. Revisa la sesión en el TUI)")
def cambiar_repo(chat, slug):