Fixing model info

This commit is contained in:
Dita Aji Pratama 2026-10-08 17:01:08 +07:00
parent 72b50811f1
commit 911e44fdd5
4 changed files with 100 additions and 35 deletions

View File

@ -2,7 +2,6 @@ import json
import threading
from datetime import datetime
import config
from lib import moop
from lib import ntro, agent_loop
from tools.vision import ImagePayload
@ -45,12 +44,39 @@ WELCOME_ART = """
╚══════════════════════════════════════════╝
"""
def log(app, role, text):
app.log.append({
def log(app, role, text, model_info=None):
entry = {
"role": role,
"text": text,
"time": datetime.now().strftime("%H:%M"),
})
}
if model_info:
# Display pair: (provider, model)
entry["model_info"] = model_info
app.log.append(entry)
def _pair(model_info):
"""(provider, model) untuk display; None jika info tidak ada."""
if not model_info:
return None
return (model_info.get("provider"), model_info.get("model"))
def _tag_ai_logs(app, prev_entries, pair):
"""Tempelkan info model yang benar-benar dipakai ke entri log AI yang baru
dibuat pada langkah ini.
Berbasis identitas entri, bukan index: entri "Thinking..." di-pop setelah
llm.chat() kembali sehingga index entri AI ikut bergeser. `prev_entries`
menahan referensi entri lama agar id-nya tidak dipakai ulang oleh Python.
"""
if not pair:
return
seen = {id(e) for e in prev_entries}
for entry in app.log:
if entry.get("role") == "ai" and id(entry) not in seen:
entry["model_info"] = pair
def submit(app, stdscr):
@ -59,12 +85,6 @@ def submit(app, stdscr):
return
log(app, "user", query)
model_info = (
moop.resolve_provider(None, app.llm.base_url, app.llm.model),
app.llm.model
)
if app.log:
app.log[-1]["model_info"] = model_info
app.input_buffer = [""]
app.input_line = 0
app.input_col = 0
@ -83,7 +103,7 @@ def submit(app, stdscr):
app._model_info(),
)
_add_msg(app, "user", query, model_info=app._model_info())
_add_msg(app, "user", query)
app.agent_done.clear()
app.agent_thread = threading.Thread(
@ -128,8 +148,15 @@ def _agent_loop(app):
skill = config.AGENT_SKILLS
tool_reminder = None
log_prev = list(app.log)
response = app.llm.chat(app.messages, tools=app.TOOLS, on_stream_chunk=on_stream_chunk, tool_reminder=tool_reminder)
# Model final: diambil dari response, bukan dari state llm saat ini,
# supaya auto-switch/rotasi key/fallback ikut tercatat dengan benar.
used = getattr(response, "model_info", None) or {}
pair = _pair(used)
akwargs = {"model_info": used} if used else {}
# Hapus "Thinking..." log
for i in range(len(app.log) - 1, -1, -1):
if app.log[i].get('role') == 'system' and 'Thinking' in app.log[i].get('text', ''):
@ -177,11 +204,12 @@ def _agent_loop(app):
if placeholder_marker in text or text == "" or text == "...":
app.log.pop(i)
_add_msg(app, "assistant", response.content, tool_calls=response.tool_calls)
_add_msg(app, "assistant", response.content, tool_calls=response.tool_calls, **akwargs)
# Log content AI dulu (penjelasan sebelum tools)
if response.content and response.content.strip():
log(app, "ai", response.content)
log(app, "ai", response.content, model_info=pair)
_tag_ai_logs(app, log_prev, pair)
# Log dan execute tool_calls
for tc in response.tool_calls:
@ -190,7 +218,7 @@ def _agent_loop(app):
log(app, "tool_call", json.dumps({
"name": tname,
"arguments": targs,
}))
}), model_info=pair)
if app.scroll_follow:
app.scroll = 999999
result = agent_loop.execute_tool(tc, app.TOOL_HANDLERS)
@ -205,8 +233,9 @@ def _agent_loop(app):
has_content = bool(response.content) or (isinstance(response.content, list) and len(response.content) > 0)
if has_content:
if isinstance(response.content, list):
log(app, "ai", "[Image generated]")
_add_msg(app, "assistant", response.content)
log(app, "ai", "[Image generated]", model_info=pair)
_add_msg(app, "assistant", response.content, **akwargs)
_tag_ai_logs(app, log_prev, pair)
log(app, "sep", "")
ntro.end(stamp)
app.agent_done.set()

View File

@ -209,22 +209,23 @@ class HendrikTUI:
if i > 0:
log(self, "sep", "")
log(self, "user", msg["content"])
mi = msg.get("model_info")
if mi:
self.log[-1]["model_info"] = (mi.get("provider"), mi.get("model"))
elif msg["role"] == "assistant":
mi = msg.get("model_info")
pair = (mi.get("provider"), mi.get("model")) if mi else None
# Log tool calls jika ada
if msg.get("tool_calls"):
for tc in msg["tool_calls"]:
log(self, "tool_call", json.dumps({
"name": tc["function"]["name"],
"arguments": tc["function"]["arguments"],
}))
}), model_info=pair)
# Log AI content jika ada dan ini adalah response final
next_role = session.messages[i + 1]["role"] if i + 1 < len(session.messages) else None
if next_role in ("user", None):
if msg.get("content"):
log(self, "ai", msg["content"])
if pair:
self.log[-1]["model_info"] = pair
elif msg["role"] == "tool":
# Tool result - tidak perlu di-log secara eksplisit
pass

View File

@ -55,6 +55,19 @@ def draw_header(app, stdscr):
draw_menubar(app, stdscr)
def _model_info_line(item):
"""Baris " Provider - Model " dari entri log; "" jika info tidak ada.
Dipakai bersama oleh blok `ai` dan `tool_call` agar tampilan konsisten."""
model_info = item.get("model_info")
if not model_info:
return ""
p_name, m_name = model_info
if p_name:
return f" {p_name} - {m_name} "
return f" {m_name} " if m_name else ""
def draw_chat(app, stdscr):
# Area chat — dari baris 2 sampai baris (h - 11).
# Bisa di-scroll dengan Page Up / Page Down.
@ -136,20 +149,13 @@ def draw_chat(app, stdscr):
_add_blank()
if role == "user":
model_info = item.get("model_info", None)
if model_info:
p_name, m_name = model_info
if p_name:
info_line = f" {p_name} - {m_name} "
else:
info_line = f" {m_name} "
else:
info_line = " Unknown - Model info not found "
_add_row([(C_USER, info_line, False)])
label = f" You ({item['time']}) "
_add_row([(C_USER, label)])
_add_row([(C_USER, f" You ({item['time']}) ")])
_wrap_render(text, indent=1, color=C_INPUT, bold=False)
elif role == "ai":
info_line = _model_info_line(item)
if info_line:
_add_row([(C_AI, info_line, False)])
label = f" {app.character_name} ({item['time']}) "
_add_row([(C_AI, label)])
_wrap_render(text, indent=1, color=C_INPUT, bold=False)
@ -165,6 +171,7 @@ def draw_chat(app, stdscr):
elif role == "tool_call":
# Format:
# (blank line)
# Ollama Cloud - gemma4:31b-cloud
# Hendrik run_bash (HH:MM) ← "Hendrik" hijau, "run_bash (HH:MM)" kuning
# { ← arguments indent 1 spasi, kuning
# "command": "ls -la"
@ -173,6 +180,9 @@ def draw_chat(app, stdscr):
# Blank line di atas (kecuali sebelumnya sudah blank dari role lain)
if idx > 0 and app.log[idx - 1]["role"] not in ("sep", "welcome"):
_add_blank()
info_line = _model_info_line(item)
if info_line:
_add_row([(C_AI, info_line, False)])
try:
tc = json.loads(text)
tname = tc["name"]

View File

@ -1,6 +1,6 @@
import json
import socket
from lib import gadget
from lib import gadget, moop
import urllib.request
import urllib.error
@ -30,11 +30,14 @@ class LLMClient:
self._chain_index = 0
class Message:
def __init__(self, msg):
def __init__(self, msg, model_info=None):
raw_content = msg.get('content', '') # Ambil konten mentah
self.content = gadget.strip_thinking(raw_content) if isinstance(raw_content, str) else raw_content # Auto-strip <thinking> dari content
self.tool_calls = msg.get('tool_calls', None) # Ambil tool calls
self.warning = None
# Model yang benar-benar dipakai menghasilkan pesan ini. Bisa berbeda
# dengan model awal permintaan karena auto-switch/fallback.
self.model_info = model_info or {}
# ------------------------------------------------------------ config ---
@ -86,6 +89,23 @@ class LLMClient:
self.model = cand.get("model", "")
self.api_key = cand.get("api_key", "")
def _model_info(self, cand=None):
"""Info model yang dipakai untuk satu permintaan.
Dari kandidat chain jika ada (provider sudah dibawa moop), jika tidak
(klien non-chain) pakai state aktif dan cari nama provider-nya."""
if cand:
return {
"provider": cand.get("provider"),
"base_url": cand.get("base_url", ""),
"model": cand.get("model", ""),
}
return {
"provider": moop.resolve_provider(None, self.base_url, self.model),
"base_url": self.base_url,
"model": self.model,
}
# ----------------------------------------------------- chain selection ---
def _pick_chain_type(self, messages):
@ -108,7 +128,7 @@ class LLMClient:
def chat(self, messages, tools=None, on_stream_chunk=None, disable_reasoning=False, tool_reminder=None):
if not self._chains: # kalau bukan chain
return self._chat_once(
result = self._chat_once(
self.base_url, self.model, self.api_key,
messages,
tools=tools,
@ -116,6 +136,8 @@ class LLMClient:
disable_reasoning=disable_reasoning,
tool_reminder=tool_reminder,
)
result.model_info = self._model_info()
return result
chain_type = self._pick_chain_type(messages)
if chain_type != self._chain_key:
@ -176,6 +198,9 @@ class LLMClient:
disable_reasoning=disable_reasoning, tool_reminder=tool_reminder,
)
self._chain_index = idx
# Catat model yang benar-benar menjawab (bukan model awal
# permintaan) supaya pemanggil bisa menampilkan data yang jujur.
result.model_info = self._model_info(cand)
return result
except _LLMFail as e:
last = e