llm: restore request logging lost in the agent-loop migration
Nightly Build / build (push) Successful in 6m50s
Nightly Build / build (push) Successful in 6m50s
The LLM-requests page has been empty since 24ee5b8: deleting
`session/handler/llm_call.rs` dropped both halves of the request log.
The kernel builds the `ModelRequest` itself and sets `log: None`, so the
`LoggingModel` decorator — wrapped once per model by `LlmManager` — wrote
every metadata row with a NULL `user_id`, while the page (and the detail
endpoint) filter on it. Nothing wrote `llm_request_payloads` at all any
more, so the payload viewer had nothing to show either.
Correlation cannot come from `LlmManager`: it builds one shared client
per model and does not know whose traffic it serves. It now comes from
the `ModelSelector`, the one component that knows both the model and the
owner: `SkaldSelector::with_log(RequestLogTarget)` wraps the model it
hands out, so metadata lands in `llm_requests` attributed to the user and
the payload lands in that user's own encrypted DB, keyed by `request_id`.
Session and frame are read off the request's `conversation`/`frame`,
which makes kernel rounds, sub-agent frames and compaction summaries all
attributed with no extra plumbing (`ModelRequest::log` stays unused).
The compactor's summariser call is attributed too, which it never was:
`try_compact`/`force_compact` now take the owner (its selector is built
per compaction, so it can carry the target).
Three tests in `llm::logging` lock this down — the owner/session/frame
columns, the error row with the provider's rejected body, and the
metadata-only path when no owner pool is available.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,7 @@ use crate::approval::ApprovalManager;
|
||||
use crate::clarification::ClarificationManager;
|
||||
use crate::config::DatetimeConfig;
|
||||
use crate::llm::LlmManager;
|
||||
use crate::llm::logging::RequestLogTarget;
|
||||
use crate::loop_adapters::activation::{SkaldActivationSource, SkaldToolActivator};
|
||||
use crate::loop_adapters::async_task::CronExecutor;
|
||||
use crate::loop_adapters::builtins::{
|
||||
@@ -132,9 +133,12 @@ impl UserLoopRuntime {
|
||||
}));
|
||||
|
||||
// The default selector has no strength requirement; every turn overrides
|
||||
// it with the agent's own (D14).
|
||||
let default_selector: Arc<dyn ModelSelector> =
|
||||
Arc::new(SkaldSelector::new(llm_manager.clone(), None));
|
||||
// it with the agent's own (D14). It carries the owner's log target, so a
|
||||
// call served by it (recovery, compaction) is still attributed.
|
||||
let default_selector: Arc<dyn ModelSelector> = Arc::new(
|
||||
SkaldSelector::new(llm_manager.clone(), None)
|
||||
.with_log(RequestLogTarget::user(user_id.clone(), pool.clone())),
|
||||
);
|
||||
|
||||
let manager = Arc::new(
|
||||
LoopManager::builder()
|
||||
@@ -208,6 +212,12 @@ impl UserLoopRuntime {
|
||||
&self.store
|
||||
}
|
||||
|
||||
/// Where this user's LLM traffic is logged: metadata in the registry
|
||||
/// (attributed to them), payloads in their own encrypted pool.
|
||||
pub fn log_target(&self) -> RequestLogTarget {
|
||||
RequestLogTarget::user(self.user_id.clone(), self.pool.clone())
|
||||
}
|
||||
|
||||
/// The conversation id of a session — the store's encoding.
|
||||
pub fn conversation(session_id: i64) -> ConversationId {
|
||||
SqliteHistory::conversation(session_id)
|
||||
@@ -259,10 +269,11 @@ impl UserLoopRuntime {
|
||||
extensions.insert(Arc::new(CallerUserId(self.user_id.clone())));
|
||||
extensions.insert(scope.clone());
|
||||
|
||||
// ── Selector: this agent's strength (D14) ──
|
||||
// ── Selector: this agent's strength (D14) + the owner's request log ──
|
||||
let strength = crate::agents::load_meta(&frame_agent).ok().and_then(|m| m.strength);
|
||||
let selector: Arc<dyn ModelSelector> =
|
||||
Arc::new(SkaldSelector::new(self.llm_manager.clone(), strength));
|
||||
let selector: Arc<dyn ModelSelector> = Arc::new(
|
||||
SkaldSelector::new(self.llm_manager.clone(), strength).with_log(self.log_target()),
|
||||
);
|
||||
|
||||
// The session's root frame; the store reuses the provisioned row.
|
||||
let frame = self
|
||||
|
||||
Reference in New Issue
Block a user