05-sep: [feat] streaming en bot OpenCode (prompt_async + entrega incremental como el terminal)
This commit is contained in:
@@ -376,6 +376,8 @@ def ok_sesion(chat):
|
|||||||
|
|
||||||
|
|
||||||
def procesar_texto(chat, texto):
|
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)
|
sid, err = ok_sesion(chat)
|
||||||
if not sid:
|
if not sid:
|
||||||
enviar(chat, f"❌ No hay sesión OpenCode disponible: {err}")
|
enviar(chat, f"❌ No hay sesión OpenCode disponible: {err}")
|
||||||
@@ -384,15 +386,68 @@ def procesar_texto(chat, texto):
|
|||||||
modelo = st.get("model")
|
modelo = st.get("model")
|
||||||
stop = typing(chat)
|
stop = typing(chat)
|
||||||
t0 = int(time.time() * 1000)
|
t0 = int(time.time() * 1000)
|
||||||
try:
|
|
||||||
data, err = enviar_mensaje(sid, texto, modelo=modelo)
|
payload = {"parts": [{"type": "text", "text": texto}]}
|
||||||
finally:
|
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()
|
stop.set()
|
||||||
if data is None:
|
enviar(chat, f"❌ Error al lanzar OpenCode: {resp}")
|
||||||
enviar(chat, f"❌ Error del servidor OpenCode: {err}")
|
|
||||||
return
|
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 = []
|
adjuntos = []
|
||||||
try:
|
try:
|
||||||
code, hist = oc_get(f"/session/{sid}/message?limit=8")
|
code, hist = oc_get(f"/session/{sid}/message?limit=8")
|
||||||
@@ -401,14 +456,10 @@ def procesar_texto(chat, texto):
|
|||||||
if os.path.isfile(r)]
|
if os.path.isfile(r)]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log(f"adjuntos error: {e}")
|
log(f"adjuntos error: {e}")
|
||||||
if rtexto:
|
|
||||||
enviar(chat, formatear_html(rtexto))
|
|
||||||
if adjuntos:
|
if adjuntos:
|
||||||
enviar(chat, f"📎 {len(adjuntos)} fichero(s) del turno:")
|
enviar(chat, f"📎 {len(adjuntos)} fichero(s) del turno:")
|
||||||
for ruta in adjuntos[:6]:
|
for ruta in adjuntos[:6]:
|
||||||
enviar_doc(chat, ruta)
|
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):
|
def cambiar_repo(chat, slug):
|
||||||
|
|||||||
Reference in New Issue
Block a user