§9 acceptance: grep-zero sweep — every internal x.ai/grok identifier renamed
The PRD's first acceptance gate now holds: grep -RinE '\bx\.ai\b|grok' crates/ --include='*.rs' → 0 matches (exempt: NOTICE and third-party license archives, README provenance, and the required 'Based on Grok Build Open Source' attribution, now sourced from version_attribution.txt). Wire-visible renames (both sides in this repo, changed in lockstep): - Auth method id 'grok.com' → 'kimi-code' (AuthMethodKind::KimiCode). - Every x.ai/* and _x.ai/* ACP ext method and meta key → kigi/* / _kigi/* (~200 names; grokShell → kigiShell). Session-file replay keeps a read-side alias for the legacy '_x.ai/session/update' method so existing updates.jsonl histories load; writes emit only the new name (both directions test-pinned). - Agent types grok-build* → kigi* with a documented legacy-prefix alias at resolution time so persisted sessions keep resolving. - ToolNamespace/BuiltinAgentName GrokBuild* → Kigi* (wire snake_case kigi/kigi_concise/kigi_hashline; schema regenerated); grok_build implementation dirs renamed to kigi*. - x-grok-* headers → x-kigi-*, __GROK_* sentinels → __KIGI_*, themes grokday/groknight → kigiday/kiginight (old persisted values fall back to the default theme), web_fetch allowlist xAI hosts → kimi.com + moonshot platforms, changelog CDN → this repo, grok-build changelog archives deleted. - BYOK default endpoint removed: [endpoints] api_base_url is now truly optional with NO default — consumers fail fast with the flag name when unset (no silent x.ai egress). Mock harnesses inject it explicitly. - System-prompt identity fixed: 'released by xAI' → 'an unofficial community CLI for Kimi' (template + regenerated encrypted form). Also repaired pre-existing grok-era test debt found by the sweep: the stale trace_classify default-model pin, the grok-pager UA label test, pty-harness stale-binary reuse and non-hermetic moonshot routing (a PTY test could previously reach the real api.moonshot.cn), and the outdated oauth fixture scope key. Gates: §9 grep 0; fmt clean; workspace check/clippy 0/0 (-D warnings); FULL cargo test --workspace: 234 suites, 21,961 passed, 0 failed; deny advisories ok.
This commit is contained in:
@@ -3,7 +3,7 @@ license = "Apache-2.0"
|
||||
name = "kigi-compaction"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
description = "Shared, transport-agnostic compaction engine for Grok chat and Grok Build."
|
||||
description = "Shared, transport-agnostic compaction engine for Kigi chat and Kigi."
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Compacted-history assembly (grok-build's rebuild structure, generic).
|
||||
//! Compacted-history assembly (kigi's rebuild structure, generic).
|
||||
//!
|
||||
//! Moved from `kigi-chat-state::compaction_utils::build_compacted_history` and
|
||||
//! made generic over a write-side item factory so any harness can assemble
|
||||
@@ -8,7 +8,7 @@
|
||||
//! [SP, UP', AGENTS_MD?, UQ_last?, recent…, summary, reminder?]
|
||||
//! ```
|
||||
//!
|
||||
//! grok-build is the canonical harness. The summary carrier text is built by
|
||||
//! kigi is the canonical harness. The summary carrier text is built by
|
||||
//! [`super::summary::format_compact_summary_content`].
|
||||
|
||||
use crate::item::CompactionItemFactory;
|
||||
@@ -80,7 +80,7 @@ pub fn assemble_compacted_history<T: CompactionItemFactory>(
|
||||
compacted.push(T::new_user(wrap_user_query(last_query.as_str())));
|
||||
}
|
||||
|
||||
// grok-build keeps the legacy `<user_query>`-wrapped continuation text and
|
||||
// kigi keeps the legacy `<user_query>`-wrapped continuation text and
|
||||
// appends the transcript hint after the continuation summary.
|
||||
let mut formatted_summary = format_compact_summary_content(&parts.compaction_summary);
|
||||
if let Some(ref hint) = parts.transcript_hint {
|
||||
@@ -145,7 +145,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grok_build_order_recent_before_summary() {
|
||||
fn kigi_order_recent_before_summary() {
|
||||
let recent = vec![MockItem::Recent("a1".into()), MockItem::Recent("t1".into())];
|
||||
let out = assemble_compacted_history(parts(recent));
|
||||
// [sys, prefix, agents_md, query, a1, t1, summary, reminder]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! grok-build's full-replace compaction pass.
|
||||
//! kigi's full-replace compaction pass.
|
||||
//!
|
||||
//! grok-build does not select a tail to keep; it summarizes the whole
|
||||
//! kigi does not select a tail to keep; it summarizes the whole
|
||||
//! conversation and rebuilds a fresh history from scratch. This module is the
|
||||
//! transport-agnostic orchestration of that pass:
|
||||
//!
|
||||
@@ -41,7 +41,7 @@ pub struct FullReplaceContext<T> {
|
||||
/// The last real user query (raw), kept verbatim post-compaction.
|
||||
pub last_user_query: Option<String>,
|
||||
/// Working tail retained verbatim (tool/subagent results from the current
|
||||
/// turn). grok-build keeps this; pass empty to drop it.
|
||||
/// turn). kigi keeps this; pass empty to drop it.
|
||||
pub recent_messages: Vec<T>,
|
||||
/// Pre-rendered `<system-reminder>` (edited files, running tasks,
|
||||
/// subagents, MCP, …). The harness builds this; we only carry it.
|
||||
@@ -99,7 +99,7 @@ pub struct FullReplaceOutput<T> {
|
||||
|
||||
/// A successful full-replace **sampling** pass (summary only, no assembly).
|
||||
///
|
||||
/// Returned by [`sample_full_replace_summary`] for harnesses (grok-build's
|
||||
/// Returned by [`sample_full_replace_summary`] for harnesses (kigi's
|
||||
/// shell) that drive the input ladder and assemble the history themselves —
|
||||
/// they build the assembly inputs (state-context system-reminder, AGENTS.md,
|
||||
/// plan-mode) *after* the LLM call, so they cannot use the bundled
|
||||
@@ -111,7 +111,7 @@ pub struct FullReplaceSummary {
|
||||
pub attempts: u32,
|
||||
}
|
||||
|
||||
/// Run grok-build's full-replace compaction pass and return the rebuilt
|
||||
/// Run kigi's full-replace compaction pass and return the rebuilt
|
||||
/// history. Pure orchestration: no triggers, no persistence, no commit.
|
||||
///
|
||||
/// - `llm_turns` — the (harness-prepared/sanitized) conversation the model
|
||||
@@ -172,7 +172,7 @@ where
|
||||
/// and report every attempt through `observer`. Returns the raw summary; the
|
||||
/// caller assembles the history (and owns the input ladder).
|
||||
///
|
||||
/// This is the seam grok-build's shell uses: it drives the verbatim → fitted →
|
||||
/// This is the seam kigi's shell uses: it drives the verbatim → fitted →
|
||||
/// lossy input ladder around this call (stepping on a
|
||||
/// [`FullReplaceError::Sampler`] with `context_overflow = true`) and assembles
|
||||
/// the compacted history afterward from inputs it gathers post-sampling.
|
||||
@@ -193,7 +193,7 @@ where
|
||||
}
|
||||
|
||||
let prompt = CompactionPrompt {
|
||||
// grok-build appends the summarization prompt as the final user
|
||||
// kigi appends the summarization prompt as the final user
|
||||
// message; there is no separate system prompt for the compaction call.
|
||||
system: String::new(),
|
||||
user: build_summary_prompt(user_context),
|
||||
@@ -366,11 +366,11 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Golden end-to-end test: a realistic conversation + a mock sampler that
|
||||
/// returns a structured summary must produce grok-build's exact compacted
|
||||
/// returns a structured summary must produce kigi's exact compacted
|
||||
/// history shape, with the LLM output cleaned and the agent-state reminder
|
||||
/// carried through as the final item.
|
||||
#[tokio::test]
|
||||
async fn full_replace_produces_grok_build_history_shape() {
|
||||
async fn full_replace_produces_kigi_history_shape() {
|
||||
let llm_turns = vec![
|
||||
MockItem::System("you are a helpful assistant".into()),
|
||||
MockItem::User("fix the login bug".into()),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! grok-build compaction configuration.
|
||||
//! kigi compaction configuration.
|
||||
//!
|
||||
//! Holds the [`FullReplaceConfig`] tunables struct (mirroring
|
||||
//! [`IntraCompactionConfig`](crate::intra_compaction::IntraCompactionConfig) /
|
||||
@@ -9,12 +9,12 @@
|
||||
|
||||
/// Default auto-compact threshold (% of context window) when no other source
|
||||
/// (env var, user config, remote per-model/global flags) sets it. Shared by
|
||||
/// grok-build and Grok chat (~85% trigger on both sides).
|
||||
/// kigi and Kigi chat (~85% trigger on both sides).
|
||||
pub const DEFAULT_AUTO_COMPACT_THRESHOLD_PERCENT: u8 = 85;
|
||||
|
||||
/// Minimum character count for a cleaned summary seed.
|
||||
///
|
||||
/// grok-build retries when the cleaned summary is shorter than this — the
|
||||
/// kigi retries when the cleaned summary is shorter than this — the
|
||||
/// smallest healthy prod summary observed was ~3,242 chars; anything under
|
||||
/// 500 is treated as degenerate and retried like a transient failure.
|
||||
pub const MIN_SUMMARY_SEED_CHARS: usize = 500;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//! LLM calls.
|
||||
//!
|
||||
//! The *policy* lives here (shared across harnesses); the per-harness error
|
||||
//! types and their wrapping (e.g. grok-build's `SamplingError` →
|
||||
//! types and their wrapping (e.g. kigi's `SamplingError` →
|
||||
//! `CompactFailure(acp::Error)`) stay in thin host wrappers that delegate the
|
||||
//! status/message decisions to these functions.
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
//! grok-build's "code agent" compaction subsystem.
|
||||
//! kigi's "code agent" compaction subsystem.
|
||||
//!
|
||||
//! grok-build does not select a tail to keep; it summarizes the whole
|
||||
//! kigi does not select a tail to keep; it summarizes the whole
|
||||
//! conversation and rebuilds a fresh history from scratch (the *full-replace*
|
||||
//! strategy). This module groups that subsystem — generic over the engine's
|
||||
//! [`CompactionItem`](crate::item::CompactionItem) /
|
||||
//! [`CompactionItemFactory`](crate::item::CompactionItemFactory) seams — so it
|
||||
//! can be reused as a unit by grok-build, separate from Grok chat's
|
||||
//! can be reused as a unit by kigi, separate from Kigi chat's
|
||||
//! [`intra_compaction`](crate::intra_compaction) (tail-keep, per-step) and
|
||||
//! [`inter_compaction`](crate::inter_compaction) (chunked, between-turn).
|
||||
//!
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
//! Observability seam for the full-replace (grok-build) pass.
|
||||
//! Observability seam for the full-replace (kigi) pass.
|
||||
//!
|
||||
//! The shared orchestrator reports per-attempt and terminal outcomes through
|
||||
//! this trait so each harness can emit its own telemetry (grok-build:
|
||||
//! this trait so each harness can emit its own telemetry (kigi:
|
||||
//! `CompactionAttempt` rows, `CompactionRetryDegraded` events, span records,
|
||||
//! request-artifact persistence) without the shared crate depending on a
|
||||
//! telemetry backend. Mirrors
|
||||
//! [`IntraCompactionObserver`](crate::intra_compaction::IntraCompactionObserver)
|
||||
//! / [`InterCompactionObserver`](crate::inter_compaction::InterCompactionObserver).
|
||||
//!
|
||||
//! Emission points are part of the behavior contract: the grok-build observer
|
||||
//! Emission points are part of the behavior contract: the kigi observer
|
||||
//! preserves the pre-migration `CompactionAttempt`/`CompactionRetryDegraded`
|
||||
//! semantics byte-for-byte.
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
//! grok-build's session-level summarization prompt.
|
||||
//! kigi's session-level summarization prompt.
|
||||
//!
|
||||
//! Split out of the crate-root `prompt` module so grok-build's full-replace
|
||||
//! Split out of the crate-root `prompt` module so kigi's full-replace
|
||||
//! prompt lives alongside the rest of its [`code_compaction`](crate::code_compaction)
|
||||
//! subsystem. The Grok chat's step-level intra prompt
|
||||
//! subsystem. The Kigi chat's step-level intra prompt
|
||||
//! ([`format_compaction_prompt`](crate::prompt::format_compaction_prompt))
|
||||
//! stays at the crate root.
|
||||
|
||||
/// Build grok-build's session-level summarization prompt (no chat history).
|
||||
/// Build kigi's session-level summarization prompt (no chat history).
|
||||
///
|
||||
/// `user_context` is the optional `/compact <text>` user-provided context,
|
||||
/// spliced inline into the structured prompt. Ported verbatim from
|
||||
@@ -49,11 +49,11 @@ your response.
|
||||
/// The prompt is owned by the harness's [`CompactionSampler`] impl (it appends
|
||||
/// the prompt as the final user message before sampling), not by the shared
|
||||
/// orchestrator. This enum lets each harness select the right one in one place
|
||||
/// so the structured (grok-build) and short self-summary prompts stay
|
||||
/// so the structured (kigi) and short self-summary prompts stay
|
||||
/// shared instead of duplicated per harness.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum SummaryPromptKind {
|
||||
/// grok-build's detailed, numbered-section summary prompt.
|
||||
/// kigi's detailed, numbered-section summary prompt.
|
||||
#[default]
|
||||
Structured,
|
||||
/// The short self-summarization prompt.
|
||||
@@ -106,7 +106,7 @@ mod tests {
|
||||
#[test]
|
||||
fn kind_structured_matches_build_summary_prompt() {
|
||||
// The Structured kind must be byte-identical to the legacy entry point
|
||||
// so routing through the selector never changes grok-build's prompt.
|
||||
// so routing through the selector never changes kigi's prompt.
|
||||
assert_eq!(
|
||||
build_summary_prompt_kind(SummaryPromptKind::Structured, None),
|
||||
build_summary_prompt(None)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! The shared bounded-retry summary-sampling loop.
|
||||
//!
|
||||
//! The canonical `sample → classify → retry` loop, used by **both** grok-build's
|
||||
//! The canonical `sample → classify → retry` loop, used by **both** kigi's
|
||||
//! full-replace pass ([`sample_full_replace_summary`](super::sample_full_replace_summary))
|
||||
//! and Grok chat's intra `Shared` summarizer
|
||||
//! and Kigi chat's intra `Shared` summarizer
|
||||
//! ([`apply_intra_compaction`](crate::intra_compaction::apply_intra_compaction)).
|
||||
//! Centralising it here removes the two near-identical copies that previously
|
||||
//! lived in `code_compaction::compact` and `intra_compaction::compact`.
|
||||
@@ -18,7 +18,7 @@
|
||||
//!
|
||||
//! The loop is *content-neutral*: callers build the prompt, map the structured
|
||||
//! [`SampleRetryError`] onto their own error type, and decide whether to clean
|
||||
//! the winning summary (grok-build cleans in its assembler; intra cleans via
|
||||
//! the winning summary (kigi cleans in its assembler; intra cleans via
|
||||
//! [`format_compact_summary`](super::format_compact_summary)). Per-attempt
|
||||
//! telemetry flows through the [`FullReplaceObserver`] seam; callers without
|
||||
//! per-attempt metrics (intra) pass `&()`.
|
||||
@@ -63,7 +63,7 @@ pub enum SampleRetryError {
|
||||
/// Whether re-sending the same input cannot help.
|
||||
deterministic: bool,
|
||||
/// Whether the failure was a context-length overflow (a deterministic
|
||||
/// signal the grok-build host uses to step down its input size).
|
||||
/// signal the kigi host uses to step down its input size).
|
||||
context_overflow: bool,
|
||||
/// Total attempts made.
|
||||
attempts: u32,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//! Moved verbatim from `kigi-chat-state`'s `compaction_utils`. Covers:
|
||||
//!
|
||||
//! - cleaning the compaction model's raw output ([`format_compact_summary`]),
|
||||
//! - the grok-build continuation carrier ([`format_compact_summary_content`]),
|
||||
//! - the kigi continuation carrier ([`format_compact_summary_content`]),
|
||||
//! - the canonical `<user_query>` wrapping ([`wrap_user_query`]).
|
||||
|
||||
/// Clean the compaction model's raw output into the plain-text `Summary:`
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Item filtering and user-query extraction for history compaction —
|
||||
//! generic over [`CompactionItem`] / [`CompactionItemBuilder`].
|
||||
//!
|
||||
//! Behavior is byte-for-byte identical for Grok chat (`T = Arc<GrokTurn>`).
|
||||
//! Behavior is byte-for-byte identical for Kigi chat (`T = Arc<KigiTurn>`).
|
||||
|
||||
use tracing::info;
|
||||
|
||||
@@ -43,7 +43,7 @@ pub fn keep_turn_for_basic_compaction<T: CompactionItem + ?Sized>(turn: &T) -> b
|
||||
/// [`CompactionItemBuilder::strip_tool_content`]).
|
||||
/// - Keep `User` items as-is (separation happens later).
|
||||
/// - Drop `System` and non-summary `Developer` items; keep prior compaction
|
||||
/// summaries so their `<grok_user_queries>` sections can be split out.
|
||||
/// summaries so their `<kigi_user_queries>` sections can be split out.
|
||||
pub fn filter_turns_for_inter_compaction<T: CompactionItemBuilder>(turns: &[T]) -> Vec<T> {
|
||||
turns
|
||||
.iter()
|
||||
@@ -73,9 +73,9 @@ pub fn filter_turns_for_inter_compaction<T: CompactionItemBuilder>(turns: &[T])
|
||||
///
|
||||
/// A prior compaction from DnC has the format:
|
||||
/// ```text
|
||||
/// <grok_user_queries>
|
||||
/// <kigi_user_queries>
|
||||
/// ...user messages...
|
||||
/// </grok_user_queries>
|
||||
/// </kigi_user_queries>
|
||||
///
|
||||
/// <chunk_summary index="0">
|
||||
/// ...
|
||||
@@ -83,13 +83,13 @@ pub fn filter_turns_for_inter_compaction<T: CompactionItemBuilder>(turns: &[T])
|
||||
/// ```
|
||||
///
|
||||
/// Returns `(all_user_messages_sections, rest)`.
|
||||
/// Extracts **all** `<grok_user_queries>...</grok_user_queries>` blocks
|
||||
/// Extracts **all** `<kigi_user_queries>...</kigi_user_queries>` blocks
|
||||
/// (there may be multiple after chained compactions) and concatenates them.
|
||||
/// Everything outside these blocks is returned as `rest`.
|
||||
/// If no blocks are found, returns `(None, full_text)`.
|
||||
pub fn split_prior_compaction_text(text: &str) -> (Option<String>, String) {
|
||||
let start_tag = "<grok_user_queries>";
|
||||
let end_tag = "</grok_user_queries>";
|
||||
let start_tag = "<kigi_user_queries>";
|
||||
let end_tag = "</kigi_user_queries>";
|
||||
|
||||
let mut user_sections = Vec::new();
|
||||
let mut rest = String::new();
|
||||
@@ -158,11 +158,11 @@ pub fn truncate_middle(msg: &str, max_chars: usize) -> Option<String> {
|
||||
Some(format!("{}...[truncated]...{}", front, back))
|
||||
}
|
||||
|
||||
/// Extract a `<grok_user_queries>` XML block from `User` items in `turns`.
|
||||
/// Extract a `<kigi_user_queries>` XML block from `User` items in `turns`.
|
||||
///
|
||||
/// Walks `turns`, finds `User` items, and formats each as a `<grok_query>`
|
||||
/// Walks `turns`, finds `User` items, and formats each as a `<kigi_query>`
|
||||
/// element with text content (from [`CompactionItem::text`]) and any
|
||||
/// `<grok_file id="..." name="..." />` lines for the item's attachment
|
||||
/// `<kigi_file id="..." name="..." />` lines for the item's attachment
|
||||
/// refs. Long user messages are truncated via [`truncate_middle`].
|
||||
///
|
||||
/// Returns `None` if no user items produced any non-empty content.
|
||||
@@ -171,7 +171,7 @@ pub fn extract_user_queries_from_turns<T: CompactionItem>(
|
||||
user_truncate_chars: u32,
|
||||
) -> Option<String> {
|
||||
let threshold = user_truncate_chars as usize;
|
||||
let mut result = String::from("<grok_user_queries>\n");
|
||||
let mut result = String::from("<kigi_user_queries>\n");
|
||||
let mut emitted_any = false;
|
||||
|
||||
for turn in turns {
|
||||
@@ -188,7 +188,7 @@ pub fn extract_user_queries_from_turns<T: CompactionItem>(
|
||||
}
|
||||
emitted_any = true;
|
||||
|
||||
result.push_str("<grok_query>");
|
||||
result.push_str("<kigi_query>");
|
||||
match truncate_middle(&text, threshold) {
|
||||
Some(truncated) => {
|
||||
info!(
|
||||
@@ -204,23 +204,23 @@ pub fn extract_user_queries_from_turns<T: CompactionItem>(
|
||||
result.push('\n');
|
||||
for att_ref in attachments {
|
||||
result.push_str(&format!(
|
||||
"<grok_file id=\"{}\" name=\"{}\" />\n",
|
||||
"<kigi_file id=\"{}\" name=\"{}\" />\n",
|
||||
att_ref.id, att_ref.name
|
||||
));
|
||||
}
|
||||
}
|
||||
result.push_str("</grok_query>\n");
|
||||
result.push_str("</kigi_query>\n");
|
||||
}
|
||||
|
||||
if !emitted_any {
|
||||
return None;
|
||||
}
|
||||
result.push_str("</grok_user_queries>");
|
||||
result.push_str("</kigi_user_queries>");
|
||||
Some(result)
|
||||
}
|
||||
|
||||
/// Walk `turns`, find any prior compaction summary items, extract their
|
||||
/// `<grok_user_queries>` blocks via [`split_prior_compaction_text`], and
|
||||
/// `<kigi_user_queries>` blocks via [`split_prior_compaction_text`], and
|
||||
/// concatenate them.
|
||||
///
|
||||
/// Returns `None` if no prior compaction items are present or none
|
||||
@@ -237,26 +237,26 @@ pub fn extract_prior_user_queries<T: CompactionItemBuilder>(turns: &[T]) -> Opti
|
||||
/// Output of [`separate_prior_user_queries`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SeparatedHistoryTurns<T> {
|
||||
/// `turns` with the `<grok_user_queries>` block stripped from every
|
||||
/// `turns` with the `<kigi_user_queries>` block stripped from every
|
||||
/// prior compaction summary item. Safe to feed to the compaction LLM —
|
||||
/// it will not re-emit the user-queries metadata.
|
||||
/// A prior compaction item whose `rest` is empty after stripping is
|
||||
/// dropped entirely.
|
||||
pub turns_for_llm: Vec<T>,
|
||||
/// Concatenation of every `<grok_user_queries>` block found (in
|
||||
/// Concatenation of every `<kigi_user_queries>` block found (in
|
||||
/// document order, joined by `\n`). `None` if no prior compaction
|
||||
/// item contained a user-queries block. Preserved verbatim so it
|
||||
/// can be passed to [`assemble_user_queries_preamble`].
|
||||
pub prior_user_queries: Option<String>,
|
||||
/// `true` if at least one prior compaction summary item was observed,
|
||||
/// regardless of whether it contained a `<grok_user_queries>` block.
|
||||
/// regardless of whether it contained a `<kigi_user_queries>` block.
|
||||
/// Used by inter-compaction to record the
|
||||
/// `ConversationCompactionCount{status="recompaction"}` metric.
|
||||
pub has_prior_compaction: bool,
|
||||
}
|
||||
|
||||
/// Walk `turns`, split every prior compaction summary item into (a) its
|
||||
/// `<grok_user_queries>` block (preserved verbatim for the next summary)
|
||||
/// `<kigi_user_queries>` block (preserved verbatim for the next summary)
|
||||
/// and (b) the rest of the summary content (rebuilt as a new summary item
|
||||
/// and forwarded to the LLM). Non-compaction items are forwarded unchanged.
|
||||
///
|
||||
@@ -264,10 +264,10 @@ pub struct SeparatedHistoryTurns<T> {
|
||||
/// handle prior compactions identically:
|
||||
///
|
||||
/// - **inter** calls this on the filtered item list before its chunking
|
||||
/// loop, so the LLM never sees `<grok_user_queries>` from earlier rounds.
|
||||
/// loop, so the LLM never sees `<kigi_user_queries>` from earlier rounds.
|
||||
/// - **intra** calls this on `turns_to_compact` for the `History` target
|
||||
/// before sampling, for the same reason. Without this stripping, the LLM
|
||||
/// would see the prior `<grok_user_queries>` and tend to copy it into the
|
||||
/// would see the prior `<kigi_user_queries>` and tend to copy it into the
|
||||
/// new summary — which then chains with the explicit preamble we prepend,
|
||||
/// snowballing across re-compactions.
|
||||
pub fn separate_prior_user_queries<T: CompactionItemBuilder>(
|
||||
@@ -293,7 +293,7 @@ pub fn separate_prior_user_queries<T: CompactionItemBuilder>(
|
||||
}
|
||||
// Matches inter's previous inline behavior (`if !rest.is_empty()`):
|
||||
// a prior compaction item whose entire content was the
|
||||
// `<grok_user_queries>` block (and therefore stripped to an empty
|
||||
// `<kigi_user_queries>` block (and therefore stripped to an empty
|
||||
// `rest`) contributes nothing for the LLM and is dropped here.
|
||||
if !rest.is_empty() {
|
||||
turns_for_llm.push(T::compaction_summary_item(rest));
|
||||
@@ -501,9 +501,9 @@ mod tests {
|
||||
fn extract_user_queries_wraps_single_user_turn() {
|
||||
let turns = vec![MockItem::user("hello world")];
|
||||
let out = extract_user_queries_from_turns(&turns, 3_000).expect("got block");
|
||||
assert!(out.starts_with("<grok_user_queries>"));
|
||||
assert!(out.ends_with("</grok_user_queries>"));
|
||||
assert!(out.contains("<grok_query>hello world</grok_query>"));
|
||||
assert!(out.starts_with("<kigi_user_queries>"));
|
||||
assert!(out.ends_with("</kigi_user_queries>"));
|
||||
assert!(out.contains("<kigi_query>hello world</kigi_query>"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -517,8 +517,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn extract_prior_user_queries_concatenates_blocks() {
|
||||
let inner = "<grok_user_queries>\n<grok_query>first</grok_query>\n</grok_user_queries>";
|
||||
let inner2 = "<grok_user_queries>\n<grok_query>second</grok_query>\n</grok_user_queries>";
|
||||
let inner = "<kigi_user_queries>\n<kigi_query>first</kigi_query>\n</kigi_user_queries>";
|
||||
let inner2 = "<kigi_user_queries>\n<kigi_query>second</kigi_query>\n</kigi_user_queries>";
|
||||
let turns = vec![MockItem::summary(inner), MockItem::summary(inner2)];
|
||||
let out = extract_prior_user_queries(&turns).expect("found prior");
|
||||
assert!(out.contains("first"));
|
||||
@@ -533,7 +533,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn separate_strips_user_queries_from_summary_item() {
|
||||
let prior = "<grok_user_queries>\n<grok_query>Q1</grok_query>\n</grok_user_queries>\n\n<chunk_summary index=\"0\">S1</chunk_summary>";
|
||||
let prior = "<kigi_user_queries>\n<kigi_query>Q1</kigi_query>\n</kigi_user_queries>\n\n<chunk_summary index=\"0\">S1</chunk_summary>";
|
||||
let turns = vec![MockItem::summary(prior), MockItem::user("Q2")];
|
||||
|
||||
let sep = separate_prior_user_queries(&turns);
|
||||
@@ -541,14 +541,14 @@ mod tests {
|
||||
assert!(sep.has_prior_compaction);
|
||||
let prior = sep.prior_user_queries.expect("prior queries extracted");
|
||||
assert!(prior.contains("Q1"));
|
||||
assert!(prior.contains("<grok_user_queries>"));
|
||||
assert!(prior.contains("<kigi_user_queries>"));
|
||||
|
||||
assert_eq!(sep.turns_for_llm.len(), 2);
|
||||
match &sep.turns_for_llm[0] {
|
||||
MockItem::Developer { text, summary } => {
|
||||
assert!(*summary);
|
||||
assert!(text.contains("<chunk_summary"));
|
||||
assert!(!text.contains("<grok_user_queries>"));
|
||||
assert!(!text.contains("<kigi_user_queries>"));
|
||||
assert!(!text.contains("Q1"));
|
||||
}
|
||||
other => panic!("expected summary item, got {:?}", other),
|
||||
@@ -558,7 +558,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn separate_drops_summary_item_with_no_rest() {
|
||||
let only_queries = "<grok_user_queries>\n<grok_query>Q</grok_query>\n</grok_user_queries>";
|
||||
let only_queries = "<kigi_user_queries>\n<kigi_query>Q</kigi_query>\n</kigi_user_queries>";
|
||||
let turns = vec![MockItem::summary(only_queries), MockItem::user("hello")];
|
||||
|
||||
let sep = separate_prior_user_queries(&turns);
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
//!
|
||||
//! Everything here is generic over [`CompactionItem`](crate::CompactionItem)
|
||||
//! / [`CompactionItemBuilder`](crate::CompactionItemBuilder) or pure
|
||||
//! string/text manipulation. Harness-bound extraction (Grok chat's
|
||||
//! `GrokConversation` traversal, `ChatCompletionRequest` user-message
|
||||
//! extraction, `GrokMessage` assembly) stays in the harness crate.
|
||||
//! string/text manipulation. Harness-bound extraction (Kigi chat's
|
||||
//! `KigiConversation` traversal, `ChatCompletionRequest` user-message
|
||||
//! extraction, `KigiMessage` assembly) stays in the harness crate.
|
||||
|
||||
pub mod filter;
|
||||
pub mod prompt;
|
||||
|
||||
@@ -12,7 +12,7 @@ pub enum CompactionStrategy {
|
||||
/// Divide turns into ≤ `dnc_chunk_token_limit` chunks, compact each,
|
||||
/// then combine the summaries into a final compaction.
|
||||
DivideAndConquer,
|
||||
/// grok-build style full-replace summarization: summarize the selected
|
||||
/// kigi style full-replace summarization: summarize the selected
|
||||
/// persisted history range with the code-compaction full-replace prompt and
|
||||
/// persist the summary as the durable conversation compaction overlay.
|
||||
FullReplace,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Compaction result validation (text-level, harness-agnostic).
|
||||
//!
|
||||
//! The Grok chat's `validate_compaction_result(GrokMessage, …)` wrapper in
|
||||
//! The Kigi chat's `validate_compaction_result(KigiMessage, …)` wrapper in
|
||||
//! the harness crate extracts the message text and delegates here.
|
||||
|
||||
use super::types::CompactionStrategy;
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
//! - **DivideAndConquer** → `config.dnc_chunk_token_limit` → N chunks.
|
||||
//!
|
||||
//! Everything else — turn filtering, prior-compaction user-query
|
||||
//! extraction, chunk summarisation, and the final `<grok_user_queries>`
|
||||
//! extraction, chunk summarisation, and the final `<kigi_user_queries>`
|
||||
//! + `<chunk_summary>` assembly — is shared. The harness supplies the
|
||||
//! candidate items, the *current* user-queries preamble (Grok chat
|
||||
//! candidate items, the *current* user-queries preamble (Kigi chat
|
||||
//! extracts it from the raw `ChatCompletionRequest`), the sampler, the
|
||||
//! token counter, and an observer for metrics.
|
||||
|
||||
@@ -40,7 +40,7 @@ const UNBOUNDED_CHUNK_LIMIT: u32 = u32::MAX;
|
||||
/// into a harness message type.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ChunkedCompactionOutput {
|
||||
/// `<grok_user_queries>` preamble + `<chunk_summary index="i">` blocks.
|
||||
/// `<kigi_user_queries>` preamble + `<chunk_summary index="i">` blocks.
|
||||
/// The harness wraps this into its summary-carrier message.
|
||||
pub combined_text: String,
|
||||
/// Thinking-channel output: `<chunk_analysis>` blocks. Empty when the
|
||||
@@ -53,7 +53,7 @@ pub struct ChunkedCompactionOutput {
|
||||
/// Steps:
|
||||
/// 1. Filter items with
|
||||
/// [`filter_turns_for_inter_compaction`](crate::history::filter::filter_turns_for_inter_compaction).
|
||||
/// 2. [`separate_prior_user_queries`] — split prior `<grok_user_queries>`
|
||||
/// 2. [`separate_prior_user_queries`] — split prior `<kigi_user_queries>`
|
||||
/// blocks out of every prior compaction summary item. The LLM never sees
|
||||
/// them. Shared with intra-compaction's `History` target so both
|
||||
/// pipelines handle re-compactions identically.
|
||||
@@ -66,11 +66,11 @@ pub struct ChunkedCompactionOutput {
|
||||
/// `thinking` channels into the analysis text.
|
||||
///
|
||||
/// `current_user_queries` is the harness-extracted preamble for *this*
|
||||
/// round's user messages (Grok chat: verbatim from the raw request, with
|
||||
/// round's user messages (Kigi chat: verbatim from the raw request, with
|
||||
/// attachment refs). `conversation_id` / `response_id` are threaded
|
||||
/// through for log correlation only.
|
||||
///
|
||||
/// Observer events (the Grok chat observer maps them to the
|
||||
/// Observer events (the Kigi chat observer maps them to the
|
||||
/// pre-unification metrics):
|
||||
/// - [`InterCompactionObserver::on_recompaction`] when prior-compaction
|
||||
/// summary items are found.
|
||||
@@ -127,7 +127,7 @@ pub async fn sample_compaction_chunked<T: CompactionItemBuilder + Send + Sync>(
|
||||
)));
|
||||
}
|
||||
|
||||
// Step 2 — split prior `<grok_user_queries>` out of every prior
|
||||
// Step 2 — split prior `<kigi_user_queries>` out of every prior
|
||||
// compaction summary item. The LLM never sees them (it would re-emit
|
||||
// them verbatim and snowball across rounds); they are reattached to
|
||||
// the final summary via `assemble_user_queries_preamble`. Shared with
|
||||
|
||||
@@ -28,7 +28,7 @@ pub struct InterCompactionConfig {
|
||||
/// (Basic strategy ignores this and emits a single chunk.)
|
||||
pub dnc_chunk_token_limit: u32,
|
||||
/// User messages with character count > this threshold are truncated
|
||||
/// (middle-cut) when assembling the `<grok_user_queries>` preamble.
|
||||
/// (middle-cut) when assembling the `<kigi_user_queries>` preamble.
|
||||
/// Applies to both Basic and DivideAndConquer.
|
||||
pub user_message_compact_threshold: u32,
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//!
|
||||
//! Harness wiring (turn selection from the conversation store, raw-request
|
||||
//! user-query extraction, summary-message assembly, persistence) stays
|
||||
//! per-harness; the Grok chat host wraps this pipeline.
|
||||
//! per-harness; the Kigi chat host wraps this pipeline.
|
||||
|
||||
pub mod compact;
|
||||
pub mod config;
|
||||
|
||||
@@ -30,7 +30,7 @@ use super::config::{IntraCompactionConfig, IntraCompactionMode, IntraSummarizer}
|
||||
use super::observer::IntraCompactionObserver;
|
||||
use super::traits::{CompactionStreamProc, CompactionTarget};
|
||||
use super::trigger::{IntraCompactionError, IntraCompactionResult, IntraCompactionTrigger};
|
||||
// The `Shared` summarizer reuses grok-build's full-replace summarization core
|
||||
// The `Shared` summarizer reuses kigi's full-replace summarization core
|
||||
// (the shared summarization core lives in `code_compaction`); intra_compaction intentionally
|
||||
// depends on `code_compaction` for it.
|
||||
use crate::code_compaction::{
|
||||
@@ -196,14 +196,14 @@ where
|
||||
.await
|
||||
}
|
||||
|
||||
/// `FullReplace` strategy (default): grok-build's full-replace — summarize the
|
||||
/// `FullReplace` strategy (default): kigi's full-replace — summarize the
|
||||
/// *whole* conversation (prior history + accumulated steps) in one pass and
|
||||
/// rebuild context from scratch via [`CompactionTarget::FullReplace`].
|
||||
///
|
||||
/// Unlike the partial modes there is no tail-keep selection and no
|
||||
/// `<grok_user_queries>` preamble: the shared `code_compaction` summarizer
|
||||
/// `<kigi_user_queries>` preamble: the shared `code_compaction` summarizer
|
||||
/// (always [`IntraSummarizer::Shared`] here, regardless of `policy.summarizer`)
|
||||
/// preserves user intent itself, matching grok-build. The reduction and
|
||||
/// preserves user intent itself, matching kigi. The reduction and
|
||||
/// `min_compactable_tokens` guards are kept for parity with the partial modes.
|
||||
pub async fn apply_full_replace_compaction<T, S, P>(
|
||||
stream_proc: &S,
|
||||
@@ -243,7 +243,7 @@ where
|
||||
"[IntraCompaction] starting full replace"
|
||||
);
|
||||
|
||||
// 2. Summarize the whole conversation through grok-build's shared core.
|
||||
// 2. Summarize the whole conversation through kigi's shared core.
|
||||
// FullReplace always uses the shared summarizer (it *is* the
|
||||
// `code_compaction` path); `policy.summarizer` is ignored for this mode.
|
||||
let summary_text = sample_shared_summary_with_retries(sampler, &source_turns, policy).await?;
|
||||
@@ -252,7 +252,7 @@ where
|
||||
// the compaction. FullReplace drops the working tail, so append the
|
||||
// harness-supplied `<system-reminder>` (verbatim ids) to the summary so
|
||||
// the model can keep polling/cancelling them. Empty/None → no change.
|
||||
// Shared with Grok chat inter-compaction via `append_reminder_block` so
|
||||
// Shared with Kigi chat inter-compaction via `append_reminder_block` so
|
||||
// both inject the reminder into the summary text identically, before the
|
||||
// reduction guard below counts it.
|
||||
let summary_text = crate::append_reminder_block(summary_text, active_reminder);
|
||||
@@ -483,10 +483,10 @@ where
|
||||
"[IntraCompaction] starting"
|
||||
);
|
||||
|
||||
// 3a. For `History` target, split prior `<grok_user_queries>` blocks
|
||||
// 3a. For `History` target, split prior `<kigi_user_queries>` blocks
|
||||
// out of any prior compaction summary items before sampling — same
|
||||
// primitive inter-compaction uses, so the LLM never sees
|
||||
// `<grok_user_queries>` and won't re-emit it (which would snowball
|
||||
// `<kigi_user_queries>` and won't re-emit it (which would snowball
|
||||
// with our explicit preamble across re-compactions). `Steps` target
|
||||
// has no user-queries semantics and skips this entirely.
|
||||
let (turns_for_llm, prior_user_queries) = match target {
|
||||
@@ -503,7 +503,7 @@ where
|
||||
// 3b. Sample the summary. The *summarization algorithm* is switchable via
|
||||
// `policy.summarizer`; everything around it — tail selection, the
|
||||
// reduction guard, the prefix-replace commit, the Steps/History modes,
|
||||
// and the `<grok_user_queries>` preamble below — stays intra's.
|
||||
// and the `<kigi_user_queries>` preamble below — stays intra's.
|
||||
let summary_text = match policy.summarizer {
|
||||
// Previous intra algorithm: per-target prompt, bounded retry, and NO
|
||||
// output cleaning — the raw model text flows straight to the preamble.
|
||||
@@ -513,7 +513,7 @@ where
|
||||
sample_compaction_with_retries(sampler, &turns_for_llm, &prompt, timeout, policy)
|
||||
.await?
|
||||
}
|
||||
// New (default): grok-build's shared summarization core from
|
||||
// New (default): kigi's shared summarization core from
|
||||
// `code_compaction` — `build_summary_prompt` + degenerate-reject +
|
||||
// `format_compact_summary` cleaning — run intra-locally.
|
||||
IntraSummarizer::Shared => {
|
||||
@@ -521,7 +521,7 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
// 3c. For `History` target, prepend a `<grok_user_queries>` preamble so
|
||||
// 3c. For `History` target, prepend a `<kigi_user_queries>` preamble so
|
||||
// the original user messages + attachment refs survive the
|
||||
// summarization. Carries forward both prior (from earlier
|
||||
// compactions) and current (from this round's `User` turns) via
|
||||
@@ -631,14 +631,14 @@ fn build_prompt_for_target(
|
||||
|
||||
/// `Shared` summarizer (default): sample through the shared retry loop
|
||||
/// [`sample_summary_with_retries`](crate::code_compaction::sample_summary_with_retries)
|
||||
/// — grok-build's summarization core (`build_summary_prompt` + bounded retry +
|
||||
/// — kigi's summarization core (`build_summary_prompt` + bounded retry +
|
||||
/// degenerate-reject + `format_compact_summary` cleaning) — then map the
|
||||
/// structured outcome onto [`IntraCompactionError`] and return the *cleaned*
|
||||
/// summary on success.
|
||||
///
|
||||
/// The classification (degenerate/empty = transient; deterministic vs transient
|
||||
/// sampler errors, incl. context-length overflow) lives in the shared loop, so
|
||||
/// intra and grok-build stay in lock-step. Outcome mapping:
|
||||
/// intra and kigi stay in lock-step. Outcome mapping:
|
||||
/// - exhausted empty/degenerate run → [`IntraCompactionError::EmptyResponse`];
|
||||
/// - deterministic sampler error (incl. context overflow) →
|
||||
/// [`IntraCompactionError::SamplerBuild`] (terminal);
|
||||
@@ -656,7 +656,7 @@ where
|
||||
T: Send + Sync,
|
||||
P: CompactionSampler<Item = T> + ?Sized,
|
||||
{
|
||||
// grok-build appends the summarization prompt as the final user message;
|
||||
// kigi appends the summarization prompt as the final user message;
|
||||
// there is no separate system prompt for the compaction call.
|
||||
let prompt = CompactionPrompt {
|
||||
system: String::new(),
|
||||
@@ -675,7 +675,7 @@ where
|
||||
)
|
||||
.await
|
||||
{
|
||||
// grok-build returns the raw summary and cleans it in its assembler;
|
||||
// kigi returns the raw summary and cleans it in its assembler;
|
||||
// intra has no assembler, so it cleans here (pre-refactor behavior).
|
||||
Ok(SampledSummary { summary, .. }) => Ok(format_compact_summary(&summary)),
|
||||
Err(SampleRetryError::Empty { .. }) => Err(IntraCompactionError::EmptyResponse),
|
||||
@@ -694,7 +694,7 @@ where
|
||||
}
|
||||
|
||||
/// Map an [`IntraCompactionError`] to a stable, low-cardinality `status`
|
||||
/// metric label. Keep these in sync with the doc string on Grok chat's
|
||||
/// metric label. Keep these in sync with the doc string on Kigi chat's
|
||||
/// `IntraCompactionCount` metric.
|
||||
pub fn error_status_label(err: &IntraCompactionError) -> &'static str {
|
||||
match err {
|
||||
@@ -753,7 +753,7 @@ where
|
||||
/// summarizer).
|
||||
///
|
||||
/// Structured variants map directly. The `Other` fallback string-matches
|
||||
/// the literal error messages produced by the Grok chat sampler —
|
||||
/// the literal error messages produced by the Kigi chat sampler —
|
||||
/// keep these in sync if either side changes (the
|
||||
/// `compaction_sample_error_to_intra*` tests below guard the mapping).
|
||||
fn compaction_sample_error_to_intra(err: CompactionSampleError) -> IntraCompactionError {
|
||||
@@ -882,7 +882,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn compaction_sample_error_to_intra_maps_empty_response() {
|
||||
// The literal message emitted by the Grok chat sampler when the
|
||||
// The literal message emitted by the Kigi chat sampler when the
|
||||
// response channel produces no content.
|
||||
let intra = compaction_sample_error_to_intra(CompactionSampleError::Other(
|
||||
anyhow::anyhow!("Compaction scheduler returned no response channel content"),
|
||||
@@ -1536,7 +1536,7 @@ mod tests {
|
||||
}
|
||||
|
||||
/// `Arc<MockItem>` also satisfies the builder bound via the blanket impl
|
||||
/// — guards the forwarding that Grok chat (`Arc<GrokTurn>`) relies on.
|
||||
/// — guards the forwarding that Kigi chat (`Arc<KigiTurn>`) relies on.
|
||||
#[test]
|
||||
fn arc_blanket_impl_forwards_builder_methods() {
|
||||
let item = Arc::new(MockItem::user("hello"));
|
||||
|
||||
@@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Which targets intra-compaction may compact.
|
||||
///
|
||||
/// - `FullReplace` (default): grok-build's full-replace strategy — summarize
|
||||
/// - `FullReplace` (default): kigi's full-replace strategy — summarize
|
||||
/// the *whole* conversation (prior history + accumulated steps) and rebuild
|
||||
/// context from scratch as `[system] + [summary]`. Drives the shared
|
||||
/// `code_compaction` summarizer directly; no tail is kept.
|
||||
@@ -56,7 +56,7 @@ pub enum IntraSummarizer {
|
||||
///
|
||||
/// When `enabled = false` (default), no intra-compaction runs for that agent.
|
||||
///
|
||||
/// Uses **percentage** thresholds (borrowed from grok-shell's
|
||||
/// Uses **percentage** thresholds (borrowed from kigi-shell's
|
||||
/// `CompactionPolicy`) for portability across models with different context
|
||||
/// windows.
|
||||
///
|
||||
@@ -65,7 +65,7 @@ pub enum IntraSummarizer {
|
||||
/// LLM call, audit) and a **mode-specific** block whose fields are each read by
|
||||
/// only a subset of modes (see the per-field `[...]` tags). In particular,
|
||||
/// `FullReplace` — the default — ignores `min_steps_before_compact` at trigger
|
||||
/// time (token threshold only, matching grok-build) and also ignores
|
||||
/// time (token threshold only, matching kigi) and also ignores
|
||||
/// `summarizer`, `target_threshold_percent`, `steps_trigger_ratio`, and
|
||||
/// `user_message_truncate_chars`. The field remains on this config for all
|
||||
/// modes (YAML / remote agent config / defaults); only enforcement is mode-dependent.
|
||||
@@ -107,7 +107,7 @@ pub struct IntraCompactionConfig {
|
||||
/// **Enforcement:** applied for `StepsOnly` / `HistoryOnly` /
|
||||
/// `HistoryThenSteps`. **Ignored** when [`mode`](Self::mode) is
|
||||
/// [`IntraCompactionMode::FullReplace`] (token threshold alone, same idea
|
||||
/// as grok-build full-replace auto-compact). Worthless early passes are
|
||||
/// as kigi full-replace auto-compact). Worthless early passes are
|
||||
/// still limited by [`min_compactable_tokens`](Self::min_compactable_tokens)
|
||||
/// / reduction guards after a trigger.
|
||||
pub min_steps_before_compact: u32,
|
||||
@@ -192,7 +192,7 @@ pub struct IntraCompactionConfig {
|
||||
// preamble). --
|
||||
/// [HistoryOnly / HistoryThenSteps] Character threshold above which an
|
||||
/// original user message gets middle-truncated when included in the
|
||||
/// `<grok_user_queries>` preamble prepended to the history compaction
|
||||
/// `<kigi_user_queries>` preamble prepended to the history compaction
|
||||
/// summary. Mirrors the inter-compaction Basic threshold. Has no
|
||||
/// effect for `Steps` target — steps compaction has no user-queries
|
||||
/// preamble. Default: `3000`. (Not always exposed by remote agent-config
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//! [`CompactionItemBuilder`](crate::CompactionItemBuilder).
|
||||
//!
|
||||
//! Harness wiring (trigger call sites, LLM transport, metrics backends,
|
||||
//! state commit) stays per-harness; the Grok chat host
|
||||
//! state commit) stays per-harness; the Kigi chat host
|
||||
//! wraps these entry points with its tokenizer + metrics observers.
|
||||
|
||||
pub mod compact;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
//! Observability seam for intra-compaction.
|
||||
//!
|
||||
//! The shared orchestrator reports terminal outcomes through this trait so
|
||||
//! each harness can emit its own metrics (Grok chat: its own metrics
|
||||
//! each harness can emit its own metrics (Kigi chat: its own metrics
|
||||
//! counters/histograms in the harness crate)
|
||||
//! without the shared crate depending on a metrics backend. Emission points
|
||||
//! and label values are part of the behavior contract — Grok chat's
|
||||
//! and label values are part of the behavior contract — Kigi chat's
|
||||
//! observer preserves them byte-for-byte.
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -20,7 +20,7 @@ pub enum CompactionTarget {
|
||||
/// inter-compaction.
|
||||
History,
|
||||
/// Replace the *whole* conversation (prior history + accumulated steps)
|
||||
/// with a single summary — grok-build's full-replace strategy. No tail is
|
||||
/// with a single summary — kigi's full-replace strategy. No tail is
|
||||
/// kept; the read-view is [`CompactionStreamProc::get_all_turns_for_compaction`].
|
||||
FullReplace,
|
||||
}
|
||||
@@ -37,8 +37,8 @@ impl CompactionTarget {
|
||||
}
|
||||
|
||||
/// Minimal interface the compaction orchestrator needs from the agent's
|
||||
/// stream processor. Implemented by Grok chat's
|
||||
/// `StreamProcessor` (`Item = Arc<GrokTurn>`).
|
||||
/// stream processor. Implemented by Kigi chat's
|
||||
/// `StreamProcessor` (`Item = Arc<KigiTurn>`).
|
||||
///
|
||||
/// Two read-views are exposed:
|
||||
///
|
||||
@@ -89,7 +89,7 @@ pub trait CompactionStreamProc: Send + Sync {
|
||||
///
|
||||
/// The `Self::Item: Send` bound lets the default hold the history vec across
|
||||
/// the second `await` while keeping the boxed future `Send`; every concrete
|
||||
/// item type (`Arc<GrokTurn>`) already satisfies it.
|
||||
/// item type (`Arc<KigiTurn>`) already satisfies it.
|
||||
async fn get_all_turns_for_compaction(&self) -> Vec<Self::Item>
|
||||
where
|
||||
Self::Item: Send,
|
||||
|
||||
@@ -111,7 +111,7 @@ pub enum IntraCompactionError {
|
||||
/// `min_steps_before_compact` remains on [`IntraCompactionConfig`] for every
|
||||
/// mode, but is **not** enforced when
|
||||
/// [`mode`](IntraCompactionConfig::mode) is
|
||||
/// [`IntraCompactionMode::FullReplace`] — that path matches grok-build's
|
||||
/// [`IntraCompactionMode::FullReplace`] — that path matches kigi's
|
||||
/// full-replace trigger (token threshold alone) so a large first-step prompt
|
||||
/// can still compact. Partial modes still gate on min steps.
|
||||
pub fn should_compact(
|
||||
@@ -200,7 +200,7 @@ mod tests {
|
||||
assert_eq!(p.mode, IntraCompactionMode::FullReplace);
|
||||
assert_eq!(p.min_steps_before_compact, 3);
|
||||
// Field is present; FullReplace only uses the token threshold
|
||||
// (parity with grok-build auto-compact).
|
||||
// (parity with kigi auto-compact).
|
||||
let t = should_compact(&p, 90_000, 100_000, 0).expect("should trigger");
|
||||
assert_eq!(t.step, 0);
|
||||
assert!(should_compact(&p, 90_000, 100_000, 2).is_some());
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
//!
|
||||
//! The shared compaction algorithms operate over a sequence of *items*
|
||||
//! (turns/messages) without knowing the concrete harness type. The chat
|
||||
//! harness implements [`CompactionItem`] for its `GrokTurn`;
|
||||
//! grok-build implements it for `kigi_sampling_types::ConversationItem`.
|
||||
//! harness implements [`CompactionItem`] for its `KigiTurn`;
|
||||
//! kigi implements it for `kigi_sampling_types::ConversationItem`.
|
||||
//!
|
||||
//! Keeping the contract minimal is deliberate: the algorithms only need
|
||||
//! enough structure to (a) classify roles, (b) read text, and (c) preserve
|
||||
@@ -14,18 +14,18 @@
|
||||
//!
|
||||
//! [`CompactionItemBuilder`] is the *constructive* extension used by the
|
||||
//! history-compaction algorithms that need to rebuild items (strip prior
|
||||
//! `<grok_user_queries>` blocks, drop tool content from assistant turns,
|
||||
//! `<kigi_user_queries>` blocks, drop tool content from assistant turns,
|
||||
//! wrap an LLM summary into a carrier item).
|
||||
|
||||
/// Harness-agnostic role of a single conversation item.
|
||||
///
|
||||
/// This is the common denominator of `GrokRole` (Grok chat) and the
|
||||
/// `ConversationItem` variants (grok-build).
|
||||
/// This is the common denominator of `KigiRole` (Kigi chat) and the
|
||||
/// `ConversationItem` variants (kigi).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CompactionRole {
|
||||
/// System prompt.
|
||||
System,
|
||||
/// Developer prompt (Grok chat) — maps to System on harnesses without a
|
||||
/// Developer prompt (Kigi chat) — maps to System on harnesses without a
|
||||
/// distinct developer role.
|
||||
Developer,
|
||||
/// A user message.
|
||||
@@ -37,8 +37,8 @@ pub enum CompactionRole {
|
||||
}
|
||||
|
||||
/// A file attached to a user item, as seen by the shared user-query
|
||||
/// extraction (`<grok_file id=".." name=".." />` lines in the
|
||||
/// `<grok_user_queries>` preamble).
|
||||
/// extraction (`<kigi_file id=".." name=".." />` lines in the
|
||||
/// `<kigi_user_queries>` preamble).
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CompactionFileRef {
|
||||
/// Stable unique id of the attachment source.
|
||||
@@ -51,8 +51,8 @@ pub struct CompactionFileRef {
|
||||
/// compaction algorithms.
|
||||
///
|
||||
/// Implementors:
|
||||
/// - Grok chat: `GrokTurn`
|
||||
/// - grok-build: `ConversationItem`
|
||||
/// - Kigi chat: `KigiTurn`
|
||||
/// - kigi: `ConversationItem`
|
||||
pub trait CompactionItem {
|
||||
/// The harness-agnostic role of this item.
|
||||
fn role(&self) -> CompactionRole;
|
||||
@@ -60,8 +60,8 @@ pub trait CompactionItem {
|
||||
/// The item's text content, if any. Tool results and assistant tool-only
|
||||
/// turns may have no text.
|
||||
///
|
||||
/// Returns an owned `String` because some harnesses (Grok chat's
|
||||
/// `GrokTurn`) compute the flattened text on demand rather than storing a
|
||||
/// Returns an owned `String` because some harnesses (Kigi chat's
|
||||
/// `KigiTurn`) compute the flattened text on demand rather than storing a
|
||||
/// borrowable slice.
|
||||
fn text(&self) -> Option<String>;
|
||||
|
||||
@@ -75,20 +75,20 @@ pub trait CompactionItem {
|
||||
/// `false` for all non-assistant items.
|
||||
fn has_tool_requests(&self) -> bool;
|
||||
|
||||
/// Whether this item carries a *prior compaction summary* (Grok chat: a
|
||||
/// Whether this item carries a *prior compaction summary* (Kigi chat: a
|
||||
/// `Developer` turn with `DeveloperPromptCategory::ConversationCompaction`).
|
||||
///
|
||||
/// The basic history filter keeps such items so earlier summaries get
|
||||
/// re-summarised instead of dropped, and `separate_prior_user_queries`
|
||||
/// strips their `<grok_user_queries>` blocks before sampling.
|
||||
/// strips their `<kigi_user_queries>` blocks before sampling.
|
||||
///
|
||||
/// Required (no default) on purpose: a forgotten implementation or a
|
||||
/// missed `Arc` forwarding would silently drop prior summaries on
|
||||
/// re-compaction.
|
||||
fn is_compaction_summary(&self) -> bool;
|
||||
|
||||
/// File attachments on a (user) item, for the `<grok_file>` lines in the
|
||||
/// `<grok_user_queries>` preamble. Empty for items without attachments.
|
||||
/// File attachments on a (user) item, for the `<kigi_file>` lines in the
|
||||
/// `<kigi_user_queries>` preamble. Empty for items without attachments.
|
||||
///
|
||||
/// Required (no default) for the same reason as
|
||||
/// [`Self::is_compaction_summary`]: silent attachment loss on compaction
|
||||
@@ -103,13 +103,13 @@ pub trait CompactionItem {
|
||||
/// through generics, never as `dyn`.
|
||||
pub trait CompactionItemBuilder: CompactionItem + Clone {
|
||||
/// Construct the item that carries a compaction summary back into the
|
||||
/// conversation (Grok chat: a `Developer` turn with category
|
||||
/// conversation (Kigi chat: a `Developer` turn with category
|
||||
/// `ConversationCompaction`). The result must satisfy
|
||||
/// `is_compaction_summary() == true`.
|
||||
fn compaction_summary_item(text: String) -> Self;
|
||||
|
||||
/// Rebuild this item keeping only user-visible content, dropping tool
|
||||
/// requests/results (Grok chat: keep only `Channel` contents of an
|
||||
/// requests/results (Kigi chat: keep only `Channel` contents of an
|
||||
/// assistant turn). Returns `None` when nothing visible remains.
|
||||
///
|
||||
/// Only meaningful for `Assistant` items; the shared filters never call
|
||||
@@ -120,18 +120,18 @@ pub trait CompactionItemBuilder: CompactionItem + Clone {
|
||||
|
||||
/// Write seam for the full-replace **assembler**
|
||||
/// ([`crate::code_compaction::assemble::assemble_compacted_history`]):
|
||||
/// constructs the typed harness items that make up grok-build's rebuilt
|
||||
/// constructs the typed harness items that make up kigi's rebuilt
|
||||
/// history.
|
||||
///
|
||||
/// This is a sibling of [`CompactionItemBuilder`], not a part of it, on
|
||||
/// purpose. `CompactionItemBuilder` is already implemented by Grok chat's
|
||||
/// `GrokTurn`; adding these constructors to it as required methods would break
|
||||
/// that impl. They are also grok-build-specific (Grok chat's tail-keep path
|
||||
/// purpose. `CompactionItemBuilder` is already implemented by Kigi chat's
|
||||
/// `KigiTurn`; adding these constructors to it as required methods would break
|
||||
/// that impl. They are also kigi-specific (Kigi chat's tail-keep path
|
||||
/// has no `user_meta` / `project_instructions` / `system_reminder` carrier
|
||||
/// concept), so they live in their own seam that only the full-replace
|
||||
/// assembler depends on.
|
||||
///
|
||||
/// The grok-build implementor (`ConversationItem`) maps each constructor to the
|
||||
/// The kigi implementor (`ConversationItem`) maps each constructor to the
|
||||
/// matching factory so the `SyntheticReason` tags the replay / spawn-time
|
||||
/// idempotence guards rely on are preserved.
|
||||
pub trait CompactionItemFactory: Sized {
|
||||
@@ -148,7 +148,7 @@ pub trait CompactionItemFactory: Sized {
|
||||
}
|
||||
|
||||
/// Forward [`CompactionItem`] through shared references so the algorithms can
|
||||
/// operate over `&[Arc<T>]` (Grok chat stores turns as `Arc<GrokTurn>`).
|
||||
/// operate over `&[Arc<T>]` (Kigi chat stores turns as `Arc<KigiTurn>`).
|
||||
impl<T: CompactionItem + ?Sized> CompactionItem for std::sync::Arc<T> {
|
||||
fn role(&self) -> CompactionRole {
|
||||
(**self).role()
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
//! stay in each product host (for example `kigi-shell`).
|
||||
//!
|
||||
//! The crate depends on **neither** a conversation-type crate nor
|
||||
//! `kigi-sampling-types`. It is decoupled from both Grok chat and
|
||||
//! grok-build hosts through a small set of trait seams:
|
||||
//! `kigi-sampling-types`. It is decoupled from both Kigi chat and
|
||||
//! kigi hosts through a small set of trait seams:
|
||||
//!
|
||||
//! - [`CompactionItem`] / [`CompactionRole`] / [`CompactionItemBuilder`] —
|
||||
//! abstracts a single turn and its reconstruction.
|
||||
@@ -21,10 +21,10 @@
|
||||
//!
|
||||
//! Compaction styles live in their own modules:
|
||||
//!
|
||||
//! - [`code_compaction`] — grok-build's whole-session **full-replace**
|
||||
//! - [`code_compaction`] — kigi's whole-session **full-replace**
|
||||
//! subsystem (prompt/summary/failure/config, assemble, orchestration).
|
||||
//! - [`intra_compaction`] — Grok chat's tail-keep, per-step pass.
|
||||
//! - [`inter_compaction`] — Grok chat's chunked, between-turn pass.
|
||||
//! - [`intra_compaction`] — Kigi chat's tail-keep, per-step pass.
|
||||
//! - [`inter_compaction`] — Kigi chat's chunked, between-turn pass.
|
||||
//!
|
||||
//! Compaction-type content (parallel subfolders): [`steps`] (the step-level
|
||||
//! prompt) and [`history`] (filtering, history prompts, validation +
|
||||
@@ -34,8 +34,8 @@
|
||||
//! [`prompt::CompactionPrompt`], [`select`] (tool-pair-safe tail-keep
|
||||
//! selection — shared by the intra `Steps` and `History` targets, so it stays
|
||||
//! neutral at the crate root rather than under `steps`), and [`reminder`]
|
||||
//! (active-agent-state `<system-reminder>` formatting shared by Grok chat and
|
||||
//! grok-build; hosts still own snapshotting and host-only sections).
|
||||
//! (active-agent-state `<system-reminder>` formatting shared by Kigi chat and
|
||||
//! kigi; hosts still own snapshotting and host-only sections).
|
||||
|
||||
pub mod code_compaction;
|
||||
pub mod history;
|
||||
@@ -57,7 +57,7 @@ pub mod token;
|
||||
/// 3. this constant
|
||||
pub use intra_compaction::DEFAULT_COMPACTION_MODEL_NAME;
|
||||
|
||||
// grok-build's full-replace subsystem now lives under `code_compaction`;
|
||||
// kigi's full-replace subsystem now lives under `code_compaction`;
|
||||
// re-exported at the crate root so consumers keep a stable public API.
|
||||
pub use code_compaction::{
|
||||
CompactedHistoryParts, DEFAULT_AUTO_COMPACT_THRESHOLD_PERCENT, FailureKind,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
//!
|
||||
//! - steps prompt → [`crate::steps::format_compaction_prompt`]
|
||||
//! - history prompts → [`crate::history::prompt`]
|
||||
//! - grok-build summary prompt → [`crate::code_compaction::build_summary_prompt`]
|
||||
//! - kigi summary prompt → [`crate::code_compaction::build_summary_prompt`]
|
||||
|
||||
/// System + user prompt pair for the compaction LLM call.
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
//! Lives at the crate root rather than under a compaction-style submodule
|
||||
//! because it is consumed by *both* compaction styles and both harnesses:
|
||||
//!
|
||||
//! - Grok chat intra FullReplace ([`crate::intra_compaction`]) and inter
|
||||
//! - Kigi chat intra FullReplace ([`crate::intra_compaction`]) and inter
|
||||
//! (appends after sampling via [`append_reminder_block`])
|
||||
//! - grok-build full-replace ([`crate::code_compaction`] assemble's
|
||||
//! - kigi full-replace ([`crate::code_compaction`] assemble's
|
||||
//! `system_reminder`)
|
||||
//!
|
||||
//! **What lives here:** pure formatting of the three **common** active-agent
|
||||
|
||||
@@ -31,7 +31,7 @@ pub struct LlmCompactionOutput {
|
||||
/// [`Self::Start`], [`Self::EmptyResponse`]) so the shared retry policy can
|
||||
/// classify without string matching. [`Self::Other`] remains for samplers
|
||||
/// that only surface an opaque error; the orchestrator falls back to
|
||||
/// matching the literal messages produced by the Grok chat sampler —
|
||||
/// matching the literal messages produced by the Kigi chat sampler —
|
||||
/// keep those literals in sync (the `compaction_sample_error_to_intra*`
|
||||
/// tests guard the mapping).
|
||||
#[derive(Debug)]
|
||||
@@ -110,7 +110,7 @@ impl CompactionSampleError {
|
||||
/// Interface for the LLM call that produces compaction summaries.
|
||||
///
|
||||
/// Used by both intra-compaction (steps/history) and inter-compaction.
|
||||
/// Implemented by each harness's sampler adapter; grok-build wires its own
|
||||
/// Implemented by each harness's sampler adapter; kigi wires its own
|
||||
/// transport.
|
||||
///
|
||||
/// Returns [`LlmCompactionOutput`] containing both response and thinking
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
//! tool results in the next prompt, which the model API rejects with a 400.
|
||||
//!
|
||||
//! This is the harness-agnostic core: it operates over any slice of
|
||||
//! [`CompactionItem`], so both Grok chat (`GrokTurn`) and grok-build
|
||||
//! [`CompactionItem`], so both Kigi chat (`KigiTurn`) and kigi
|
||||
//! (`ConversationItem`) share one implementation.
|
||||
|
||||
use crate::item::CompactionItem;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Your task is to create a detailed summary of the Grok Chat conversation so far, paying close attention to the user's explicit requests and all previous actions as Grok (built by xAI).
|
||||
Your task is to create a detailed summary of the conversation so far, paying close attention to the user's explicit requests and all previous actions as Kigi.
|
||||
This summary should be thorough in capturing technical details, code patterns, architectural decisions, tool chains, and verification steps that would be essential for continuing development, research, or complex tasks without losing context.
|
||||
|
||||
Important Clarification on Terminology (Broad File Definition):
|
||||
@@ -9,12 +9,12 @@ Throughout this prompt, the term "file", "files", "file IDs", "file names", and
|
||||
- rendered image or content outputs
|
||||
- Any other file-like content, media objects, visual artifacts, or structured content blocks that have appeared in the conversation history (including but not limited to uploads, generations, render components, or persistent references).
|
||||
|
||||
Only include information that is visible in the direct user-Grok conversation history (user messages + Grok's responses, reasoning, tool calls, and tool outputs). Do not include any internal team communication, chatroom messages, or multi-agent interactions.
|
||||
Only include information that is visible in the direct user-Kigi conversation history (user messages + Kigi's responses, reasoning, tool calls, and tool outputs). Do not include any internal team communication, chatroom messages, or multi-agent interactions.
|
||||
|
||||
Use your internal thinking channel to chronologically analyze each message and section of the conversation before producing the final summary, and ensure you've covered all necessary points.
|
||||
During that analysis, thoroughly identify:
|
||||
- The user's explicit requests and evolving intents
|
||||
- Grok's approach to addressing them: reasoning steps, specific tool calls (including parallel calls), parameters, results, and how they were interpreted/synthesized (truth-seeking emphasis)
|
||||
- Kigi's approach to addressing them: reasoning steps, specific tool calls (including parallel calls), parameters, results, and how they were interpreted/synthesized (truth-seeking emphasis)
|
||||
- Key decisions, technical concepts, code patterns, and architectural choices
|
||||
- Specific details like:
|
||||
- file IDs, attachment IDs, image references/URLs, render_result IDs (if any)
|
||||
@@ -33,7 +33,7 @@ Your final summary must contain the following sections, in order:
|
||||
|
||||
1. Primary Request and Intent: Capture all of the user's explicit requests and intents in detail, including any evolution over the conversation.
|
||||
|
||||
2. Key Technical Concepts: List all important technical concepts, technologies, frameworks, and Grok-specific tool patterns discussed.
|
||||
2. Key Technical Concepts: List all important technical concepts, technologies, frameworks, and Kigi-specific tool patterns discussed.
|
||||
|
||||
3. Tool Usage & Verification: Summarize significant tool calls (code_execution REPL state, web_search, browse_page, X tools, etc.), key information retrieved/verified, cross-referencing steps, and how they influenced decisions or responses.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Your task is to create a detailed summary of the Grok Chat conversation so far, paying close attention to the user's explicit requests and all previous actions as Grok (built by xAI).
|
||||
Your task is to create a detailed summary of the conversation so far, paying close attention to the user's explicit requests and all previous actions as Kigi.
|
||||
This summary should be thorough in capturing technical details, code patterns, architectural decisions, tool chains, and verification steps that would be essential for continuing development, research, or complex tasks without losing context.
|
||||
|
||||
Important Clarification on Terminology (Broad File Definition):
|
||||
@@ -9,12 +9,12 @@ Throughout this prompt, the term "file", "files", "file IDs", "file names", and
|
||||
- rendered image or content outputs
|
||||
- Any other file-like content, media objects, visual artifacts, or structured content blocks that have appeared in the conversation history (including but not limited to uploads, generations, render components, or persistent references).
|
||||
|
||||
Only include information that is visible in the direct user-Grok conversation history (user messages + Grok's responses, reasoning, tool calls, and tool outputs). Do not include any internal team communication, chatroom messages, or multi-agent interactions.
|
||||
Only include information that is visible in the direct user-Kigi conversation history (user messages + Kigi's responses, reasoning, tool calls, and tool outputs). Do not include any internal team communication, chatroom messages, or multi-agent interactions.
|
||||
|
||||
Use your internal thinking channel to chronologically analyze each message and section of the conversation before producing the final summary, and ensure you've covered all necessary points.
|
||||
During that analysis, thoroughly identify:
|
||||
- The user's explicit requests and evolving intents
|
||||
- Grok's approach to addressing them: reasoning steps, specific tool calls (including parallel calls), parameters, results, and how they were interpreted/synthesized (truth-seeking emphasis)
|
||||
- Kigi's approach to addressing them: reasoning steps, specific tool calls (including parallel calls), parameters, results, and how they were interpreted/synthesized (truth-seeking emphasis)
|
||||
- Key decisions, technical concepts, code patterns, and architectural choices
|
||||
- Specific details like:
|
||||
- file IDs, attachment IDs, image references/URLs, render_result IDs (if any)
|
||||
@@ -33,7 +33,7 @@ Your final summary must contain the following sections, in order:
|
||||
|
||||
1. Primary Request and Intent: Capture all of the user's explicit requests and intents in detail, including any evolution over the conversation.
|
||||
|
||||
2. Key Technical Concepts: List all important technical concepts, technologies, frameworks, and Grok-specific tool patterns discussed.
|
||||
2. Key Technical Concepts: List all important technical concepts, technologies, frameworks, and Kigi-specific tool patterns discussed.
|
||||
|
||||
3. Tool Usage & Verification: Summarize significant tool calls (code_execution REPL state, web_search, browse_page, X tools, etc.), key information retrieved/verified, cross-referencing steps, and how they influenced decisions or responses.
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
//! Budgeting math in the shared engine needs a *trusted* token count, but the
|
||||
//! two harnesses disagree on how to produce one:
|
||||
//!
|
||||
//! - Grok chat has a real tokenizer (`TextTokenizer` / `ImageTokenizer`) and
|
||||
//! counts whole turns via `GrokTurn::get_num_tokens`.
|
||||
//! - grok-build estimates with `bytes / 4`.
|
||||
//! - Kigi chat has a real tokenizer (`TextTokenizer` / `ImageTokenizer`) and
|
||||
//! counts whole turns via `KigiTurn::get_num_tokens`.
|
||||
//! - kigi estimates with `bytes / 4`.
|
||||
//!
|
||||
//! Rather than bake either policy into the shared crate, callers supply an
|
||||
//! [`ItemTokenCounter`]. This keeps the engine deterministic and testable
|
||||
@@ -13,7 +13,7 @@
|
||||
//!
|
||||
//! There is intentionally **no** blanket `Arc` forwarding here: each harness
|
||||
//! implements the counter directly for the item type its algorithms run on
|
||||
//! (Grok chat: `ItemTokenCounter<Arc<GrokTurn>>`), so exactly one mechanism
|
||||
//! (Kigi chat: `ItemTokenCounter<Arc<KigiTurn>>`), so exactly one mechanism
|
||||
//! is in play.
|
||||
|
||||
/// Counts tokens for a single conversation item on behalf of the shared
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! JSON-RPC 2.0 envelope types with the Grok `session_id` / `seq`
|
||||
//! JSON-RPC 2.0 envelope types with the Kigi `session_id` / `seq`
|
||||
//! extensions.
|
||||
//!
|
||||
//! Two distinct id concepts coexist in this crate:
|
||||
@@ -116,7 +116,7 @@ impl fmt::Display for JsonRpcId {
|
||||
pub struct JsonRpcRequest<P = serde_json::Value> {
|
||||
pub jsonrpc: JsonRpcVersion,
|
||||
pub id: JsonRpcId,
|
||||
/// Grok extension: routing/sanity-check session id.
|
||||
/// Kigi extension: routing/sanity-check session id.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub session_id: Option<SessionId>,
|
||||
pub method: String,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! Receivers SHOULD switch on `data.code` (the snake_case string) rather
|
||||
//! than the numeric JSON-RPC `error.code`. The numeric is the JSON-RPC
|
||||
//! envelope code; the string is the Grok stable identifier.
|
||||
//! envelope code; the string is the Kigi stable identifier.
|
||||
//!
|
||||
//! Implemented as a `&'static [(i32, &'static str)]` table; the table is
|
||||
//! a small fixed set so a linear scan is faster than any
|
||||
|
||||
@@ -104,13 +104,13 @@ mod tests {
|
||||
fn turn_started_round_trip() {
|
||||
let event = SessionEvent::TurnStarted {
|
||||
turn_number: 1,
|
||||
model_id: "grok-3".into(),
|
||||
model_id: "kigi-3".into(),
|
||||
yolo_mode: true,
|
||||
};
|
||||
let v = serde_json::to_value(&event).unwrap();
|
||||
assert_eq!(v["event_type"], "turn_started");
|
||||
assert_eq!(v["turn_number"], 1);
|
||||
assert_eq!(v["model_id"], "grok-3");
|
||||
assert_eq!(v["model_id"], "kigi-3");
|
||||
assert_eq!(v["yolo_mode"], true);
|
||||
let back: SessionEvent = serde_json::from_value(v).unwrap();
|
||||
assert_eq!(back, event);
|
||||
@@ -121,14 +121,14 @@ mod tests {
|
||||
let v = json!({
|
||||
"event_type": "turn_started",
|
||||
"turn_number": 5,
|
||||
"model_id": "grok-3",
|
||||
"model_id": "kigi-3",
|
||||
});
|
||||
let event: SessionEvent = serde_json::from_value(v).unwrap();
|
||||
assert_eq!(
|
||||
event,
|
||||
SessionEvent::TurnStarted {
|
||||
turn_number: 5,
|
||||
model_id: "grok-3".into(),
|
||||
model_id: "kigi-3".into(),
|
||||
yolo_mode: false,
|
||||
}
|
||||
);
|
||||
@@ -141,7 +141,7 @@ mod tests {
|
||||
outcome: TurnHookOutcome::Completed,
|
||||
duration_ms: 2500,
|
||||
tool_call_count: 7,
|
||||
model_id: "grok-3".into(),
|
||||
model_id: "kigi-3".into(),
|
||||
};
|
||||
let v = serde_json::to_value(&event).unwrap();
|
||||
assert_eq!(v["event_type"], "turn_ended");
|
||||
@@ -342,7 +342,7 @@ mod tests {
|
||||
let v = json!({
|
||||
"event_type": "turn_started",
|
||||
"turn_number": 1,
|
||||
"model_id": "grok-3",
|
||||
"model_id": "kigi-3",
|
||||
"future_field": "should be ignored",
|
||||
});
|
||||
let event: SessionEvent = serde_json::from_value(v).unwrap();
|
||||
@@ -350,7 +350,7 @@ mod tests {
|
||||
event,
|
||||
SessionEvent::TurnStarted {
|
||||
turn_number: 1,
|
||||
model_id: "grok-3".into(),
|
||||
model_id: "kigi-3".into(),
|
||||
yolo_mode: false,
|
||||
}
|
||||
);
|
||||
@@ -365,7 +365,7 @@ mod tests {
|
||||
"turn_number": 1,
|
||||
"duration_ms": 100,
|
||||
"tool_call_count": 0,
|
||||
"model_id": "grok-3",
|
||||
"model_id": "kigi-3",
|
||||
// missing "outcome"
|
||||
});
|
||||
assert!(serde_json::from_value::<SessionEvent>(v).is_err());
|
||||
|
||||
@@ -37,7 +37,7 @@ fn default_schema_version() -> String {
|
||||
pub struct BeforeTurnPayload {
|
||||
/// Monotonically increasing turn counter within the session.
|
||||
pub turn_number: u64,
|
||||
/// Model being used for this turn (e.g. "grok-3").
|
||||
/// Model being used for this turn (e.g. "kigi-3").
|
||||
pub model_id: String,
|
||||
/// Whether the session is in YOLO / auto-approve mode.
|
||||
#[serde(default)]
|
||||
@@ -252,7 +252,7 @@ mod tests {
|
||||
fn before_turn_round_trip() {
|
||||
let payload = BeforeTurnPayload {
|
||||
turn_number: 42,
|
||||
model_id: "grok-3".to_string(),
|
||||
model_id: "kigi-3".to_string(),
|
||||
yolo_mode: true,
|
||||
conversation_message_count: 9,
|
||||
session_relationship: "subagent".to_string(),
|
||||
@@ -264,7 +264,7 @@ mod tests {
|
||||
serialized,
|
||||
json!({
|
||||
"turn_number": 42,
|
||||
"model_id": "grok-3",
|
||||
"model_id": "kigi-3",
|
||||
"yolo_mode": true,
|
||||
"conversation_message_count": 9,
|
||||
"session_relationship": "subagent",
|
||||
@@ -280,7 +280,7 @@ mod tests {
|
||||
fn before_turn_yolo_mode_defaults_false() {
|
||||
let json = json!({
|
||||
"turn_number": 1,
|
||||
"model_id": "grok-3",
|
||||
"model_id": "kigi-3",
|
||||
});
|
||||
let payload: BeforeTurnPayload = serde_json::from_value(json).unwrap();
|
||||
assert!(!payload.yolo_mode);
|
||||
@@ -295,7 +295,7 @@ mod tests {
|
||||
outcome: TurnHookOutcome::Completed,
|
||||
duration_ms: 1500,
|
||||
tool_call_count: 3,
|
||||
model_id: "grok-3".to_string(),
|
||||
model_id: "kigi-3".to_string(),
|
||||
written_repo_paths: vec!["outputs/result.md".to_string()],
|
||||
cancellation_category: None,
|
||||
cancellation_context: None,
|
||||
@@ -309,7 +309,7 @@ mod tests {
|
||||
"outcome": "completed",
|
||||
"duration_ms": 1500,
|
||||
"tool_call_count": 3,
|
||||
"model_id": "grok-3",
|
||||
"model_id": "kigi-3",
|
||||
"written_repo_paths": ["outputs/result.md"],
|
||||
})
|
||||
);
|
||||
@@ -325,7 +325,7 @@ mod tests {
|
||||
"outcome": "completed",
|
||||
"duration_ms": 10,
|
||||
"tool_call_count": 0,
|
||||
"model_id": "grok-3",
|
||||
"model_id": "kigi-3",
|
||||
});
|
||||
let payload: AfterTurnPayload = serde_json::from_value(json).unwrap();
|
||||
assert!(payload.written_repo_paths.is_empty());
|
||||
@@ -338,7 +338,7 @@ mod tests {
|
||||
outcome: TurnHookOutcome::Cancelled,
|
||||
duration_ms: 200,
|
||||
tool_call_count: 1,
|
||||
model_id: "grok-4".to_string(),
|
||||
model_id: "kigi-4".to_string(),
|
||||
written_repo_paths: vec![],
|
||||
cancellation_category: Some("doom_loop_repetition".to_string()),
|
||||
cancellation_context: Some(json!({ "reason": "repetition" })),
|
||||
@@ -410,7 +410,7 @@ mod tests {
|
||||
"turn_number": 1,
|
||||
"duration_ms": 100,
|
||||
"tool_call_count": 0,
|
||||
"model_id": "grok-3",
|
||||
"model_id": "kigi-3",
|
||||
});
|
||||
assert!(serde_json::from_value::<AfterTurnPayload>(json).is_err());
|
||||
}
|
||||
@@ -419,7 +419,7 @@ mod tests {
|
||||
fn extra_fields_ignored() {
|
||||
let json = json!({
|
||||
"turn_number": 1,
|
||||
"model_id": "grok-3",
|
||||
"model_id": "kigi-3",
|
||||
"future_field": "should be ignored",
|
||||
});
|
||||
let payload: BeforeTurnPayload = serde_json::from_value(json).unwrap();
|
||||
@@ -430,7 +430,7 @@ mod tests {
|
||||
fn before_turn_yolo_false_serialized() {
|
||||
let payload = BeforeTurnPayload {
|
||||
turn_number: 1,
|
||||
model_id: "grok-3".to_string(),
|
||||
model_id: "kigi-3".to_string(),
|
||||
yolo_mode: false,
|
||||
conversation_message_count: 0,
|
||||
session_relationship: "primary".to_string(),
|
||||
@@ -449,7 +449,7 @@ mod tests {
|
||||
fn turn_hook_request_before_round_trip() {
|
||||
let req = TurnHookRequest::Before(BeforeTurnPayload {
|
||||
turn_number: 7,
|
||||
model_id: "grok-3".to_string(),
|
||||
model_id: "kigi-3".to_string(),
|
||||
yolo_mode: true,
|
||||
conversation_message_count: 0,
|
||||
session_relationship: "primary".to_string(),
|
||||
@@ -461,7 +461,7 @@ mod tests {
|
||||
json!({
|
||||
"phase": "before",
|
||||
"turn_number": 7,
|
||||
"model_id": "grok-3",
|
||||
"model_id": "kigi-3",
|
||||
"yolo_mode": true,
|
||||
"conversation_message_count": 0,
|
||||
"session_relationship": "primary",
|
||||
@@ -479,7 +479,7 @@ mod tests {
|
||||
outcome: TurnHookOutcome::Completed,
|
||||
duration_ms: 10,
|
||||
tool_call_count: 2,
|
||||
model_id: "grok-3".to_string(),
|
||||
model_id: "kigi-3".to_string(),
|
||||
written_repo_paths: Vec::new(),
|
||||
cancellation_category: None,
|
||||
cancellation_context: None,
|
||||
@@ -605,7 +605,7 @@ mod tests {
|
||||
fn before_turn_legacy_payload_defaults_new_fields() {
|
||||
let json = json!({
|
||||
"turn_number": 3,
|
||||
"model_id": "grok-3",
|
||||
"model_id": "kigi-3",
|
||||
"yolo_mode": true,
|
||||
});
|
||||
let payload: BeforeTurnPayload = serde_json::from_value(json).unwrap();
|
||||
@@ -623,7 +623,7 @@ mod tests {
|
||||
"outcome": "completed",
|
||||
"duration_ms": 10,
|
||||
"tool_call_count": 0,
|
||||
"model_id": "grok-3",
|
||||
"model_id": "kigi-3",
|
||||
});
|
||||
let payload: AfterTurnPayload = serde_json::from_value(json).unwrap();
|
||||
assert_eq!(payload.cancellation_category, None);
|
||||
|
||||
@@ -12,8 +12,8 @@ fn tool_id_accepts_bare_and_namespaced_names() {
|
||||
let bare = ToolId::new("read_file").unwrap();
|
||||
assert_eq!(bare.as_str(), "read_file");
|
||||
|
||||
let namespaced = ToolId::new("GrokBuild:read_file").unwrap();
|
||||
assert_eq!(namespaced.as_str(), "GrokBuild:read_file");
|
||||
let namespaced = ToolId::new("Kigi:read_file").unwrap();
|
||||
assert_eq!(namespaced.as_str(), "Kigi:read_file");
|
||||
|
||||
assert_eq!(
|
||||
ToolId::from_str("github:list_repos").unwrap().as_str(),
|
||||
@@ -81,8 +81,8 @@ fn tool_id_rejects_empty_segments_around_separator() {
|
||||
|
||||
#[test]
|
||||
fn tool_id_try_from_string_works() {
|
||||
let id: ToolId = "GrokBuild:read_file".to_owned().try_into().unwrap();
|
||||
assert_eq!(id.as_str(), "GrokBuild:read_file");
|
||||
let id: ToolId = "Kigi:read_file".to_owned().try_into().unwrap();
|
||||
assert_eq!(id.as_str(), "Kigi:read_file");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -116,15 +116,15 @@ fn server_id_synthesis_starts_with_auto_prefix() {
|
||||
let synth = ServerId::synthesize_for_tool(&conn, &bare);
|
||||
assert_eq!(synth.as_str(), "auto:tool:read_file");
|
||||
|
||||
let namespaced = ToolId::new("GrokBuild:read_file").unwrap();
|
||||
let namespaced = ToolId::new("Kigi:read_file").unwrap();
|
||||
let synth_ns = ServerId::synthesize_for_tool(&conn, &namespaced);
|
||||
assert_eq!(synth_ns.as_str(), "auto:tool:GrokBuild:read_file");
|
||||
assert_eq!(synth_ns.as_str(), "auto:tool:Kigi:read_file");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn server_id_synthesis_is_deterministic() {
|
||||
let conn = ConnectionId::new("conn-abc").unwrap();
|
||||
let tool = ToolId::new("GrokBuild:read_file").unwrap();
|
||||
let tool = ToolId::new("Kigi:read_file").unwrap();
|
||||
let a = ServerId::synthesize_for_tool(&conn, &tool);
|
||||
let b = ServerId::synthesize_for_tool(&conn, &tool);
|
||||
assert_eq!(
|
||||
|
||||
@@ -217,7 +217,7 @@ fn full_call_envelope_serialises_to_expected_shape() {
|
||||
method: "tool.call".to_owned(),
|
||||
params: ToolCallParams {
|
||||
tool_call_id: ToolCallId::new("call_xyz").unwrap(),
|
||||
tool_id: ToolId::new("GrokBuild:read_file").unwrap(),
|
||||
tool_id: ToolId::new("Kigi:read_file").unwrap(),
|
||||
arguments: json!({"path": "/etc/hosts"}),
|
||||
deadline_ms: None,
|
||||
behavior_version: None,
|
||||
@@ -230,7 +230,7 @@ fn full_call_envelope_serialises_to_expected_shape() {
|
||||
assert_eq!(v["id"], json!("req-9c4f"));
|
||||
assert_eq!(v["session_id"], json!("sess_abc"));
|
||||
assert_eq!(v["method"], json!("tool.call"));
|
||||
assert_eq!(v["params"]["tool_id"], json!("GrokBuild:read_file"));
|
||||
assert_eq!(v["params"]["tool_id"], json!("Kigi:read_file"));
|
||||
assert_eq!(v["params"]["tool_call_id"], json!("call_xyz"));
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ fn user() -> UserId {
|
||||
}
|
||||
|
||||
fn tool() -> ToolId {
|
||||
ToolId::new("GrokBuild:read_file").unwrap()
|
||||
ToolId::new("Kigi:read_file").unwrap()
|
||||
}
|
||||
|
||||
fn server() -> ServerId {
|
||||
@@ -60,7 +60,7 @@ fn call_id() -> ToolCallId {
|
||||
fn id_types_round_trip_as_bare_strings() {
|
||||
assert_eq!(roundtrip(&session()), json!("sess_abc"));
|
||||
assert_eq!(roundtrip(&user()), json!("user_123"));
|
||||
assert_eq!(roundtrip(&tool()), json!("GrokBuild:read_file"));
|
||||
assert_eq!(roundtrip(&tool()), json!("Kigi:read_file"));
|
||||
assert_eq!(roundtrip(&server()), json!("srv-uuidv7"));
|
||||
assert_eq!(
|
||||
roundtrip(&ConnectionId::new("conn_42").unwrap()),
|
||||
@@ -257,7 +257,7 @@ fn tool_registration_round_trips_with_and_without_server_id() {
|
||||
sessions: Some(vec![session()]),
|
||||
user_id: user(),
|
||||
server_id: None,
|
||||
description: sample_description("read_file", Some("GrokBuild")),
|
||||
description: sample_description("read_file", Some("Kigi")),
|
||||
input_schema: Some(json!({"type": "object"})),
|
||||
capabilities: None,
|
||||
notification_schemas: None,
|
||||
@@ -392,7 +392,7 @@ fn registration_outcome_variants_round_trip() {
|
||||
for (outcome, expected_tag) in cases {
|
||||
let json = roundtrip(&outcome);
|
||||
assert_eq!(json["outcome"], json!(expected_tag));
|
||||
assert_eq!(json["tool_id"], json!("GrokBuild:read_file"));
|
||||
assert_eq!(json["tool_id"], json!("Kigi:read_file"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -847,7 +847,7 @@ fn tool_registration_round_trips() {
|
||||
sessions: Some(vec![session()]),
|
||||
user_id: user(),
|
||||
server_id: None,
|
||||
description: ToolDescription::new("read_file", "d").with_namespace("GrokBuild"),
|
||||
description: ToolDescription::new("read_file", "d").with_namespace("Kigi"),
|
||||
input_schema: None,
|
||||
capabilities: None,
|
||||
notification_schemas: None,
|
||||
@@ -936,13 +936,13 @@ fn session_lifecycle_payloads_round_trip() {
|
||||
let bind_result = SessionBindServerResult {
|
||||
tools: vec![ToolDescription::new("my_tool", "desc")],
|
||||
binary_version: Some("1.0.15".to_owned()),
|
||||
unserved_tool_ids: vec!["GrokBuild:monitor".to_owned()],
|
||||
unserved_tool_ids: vec!["Kigi:monitor".to_owned()],
|
||||
resolve_error: Some("missing_tool_config: no explicit tool configuration".to_owned()),
|
||||
};
|
||||
let v = roundtrip(&bind_result);
|
||||
assert_eq!(v["tools"].as_array().unwrap().len(), 1);
|
||||
assert_eq!(v["binary_version"], json!("1.0.15"));
|
||||
assert_eq!(v["unserved_tool_ids"], json!(["GrokBuild:monitor"]));
|
||||
assert_eq!(v["unserved_tool_ids"], json!(["Kigi:monitor"]));
|
||||
assert_eq!(
|
||||
v["resolve_error"],
|
||||
json!("missing_tool_config: no explicit tool configuration")
|
||||
@@ -1140,7 +1140,7 @@ fn hook_frame_round_trips_with_optional_fields() {
|
||||
fn session_event_turn_started_round_trips_via_re_export() {
|
||||
let event = SessionEvent::TurnStarted {
|
||||
turn_number: 10,
|
||||
model_id: "grok-3".into(),
|
||||
model_id: "kigi-3".into(),
|
||||
yolo_mode: true,
|
||||
};
|
||||
let v = roundtrip(&event);
|
||||
@@ -1196,8 +1196,8 @@ fn tools_changed_round_trips_with_per_array_skip_when_empty() {
|
||||
updated: vec![tool()],
|
||||
};
|
||||
let v = roundtrip(&populated);
|
||||
assert_eq!(v["added"][0], json!("GrokBuild:read_file"));
|
||||
assert_eq!(v["updated"][0], json!("GrokBuild:read_file"));
|
||||
assert_eq!(v["added"][0], json!("Kigi:read_file"));
|
||||
assert_eq!(v["updated"][0], json!("Kigi:read_file"));
|
||||
assert!(!v.as_object().unwrap().contains_key("removed"));
|
||||
}
|
||||
|
||||
@@ -1479,12 +1479,12 @@ fn tool_error_wire_render_limited_omits_card_id_when_none() {
|
||||
#[test]
|
||||
fn tool_error_wire_terminal_error_round_trips_with_string_code() {
|
||||
let err = ToolErrorWire::TerminalError {
|
||||
tool_id: ToolId::new("GrokBuild:bash").unwrap(),
|
||||
tool_id: ToolId::new("Kigi:bash").unwrap(),
|
||||
message: "exit 137".to_owned(),
|
||||
};
|
||||
let v = roundtrip(&err);
|
||||
assert_eq!(v["code"], json!("terminal_error"));
|
||||
assert_eq!(v["tool_id"], json!("GrokBuild:bash"));
|
||||
assert_eq!(v["tool_id"], json!("Kigi:bash"));
|
||||
assert_eq!(v["message"], json!("exit 137"));
|
||||
}
|
||||
|
||||
|
||||
@@ -26,10 +26,8 @@ fn bare_name_derives_to_tool_id_without_namespace() {
|
||||
|
||||
#[test]
|
||||
fn namespaced_name_derives_to_namespaced_tool_id() {
|
||||
let derived = entry("read_file", Some("GrokBuild"))
|
||||
.derive_tool_id()
|
||||
.unwrap();
|
||||
assert_eq!(derived, ToolId::new("GrokBuild:read_file").unwrap());
|
||||
let derived = entry("read_file", Some("Kigi")).derive_tool_id().unwrap();
|
||||
assert_eq!(derived, ToolId::new("Kigi:read_file").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -58,9 +56,9 @@ fn empty_name_yields_empty_id_error() {
|
||||
#[test]
|
||||
fn duplicate_derivations_in_a_batch_are_detectable() {
|
||||
let batch = [
|
||||
entry("read_file", Some("GrokBuild")),
|
||||
entry("write_file", Some("GrokBuild")),
|
||||
entry("read_file", Some("GrokBuild")),
|
||||
entry("read_file", Some("Kigi")),
|
||||
entry("write_file", Some("Kigi")),
|
||||
entry("read_file", Some("Kigi")),
|
||||
];
|
||||
|
||||
let mut seen = std::collections::HashMap::new();
|
||||
@@ -76,15 +74,13 @@ fn duplicate_derivations_in_a_batch_are_detectable() {
|
||||
}
|
||||
assert_eq!(duplicates.len(), 1, "exactly one duplicate id expected");
|
||||
let (id, indices) = &duplicates[0];
|
||||
assert_eq!(id, &ToolId::new("GrokBuild:read_file").unwrap());
|
||||
assert_eq!(id, &ToolId::new("Kigi:read_file").unwrap());
|
||||
assert_eq!(indices, &vec![0, 2]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derivation_does_not_collide_across_namespaces() {
|
||||
let a = entry("read_file", Some("GrokBuild"))
|
||||
.derive_tool_id()
|
||||
.unwrap();
|
||||
let a = entry("read_file", Some("Kigi")).derive_tool_id().unwrap();
|
||||
let b = entry("read_file", Some("github")).derive_tool_id().unwrap();
|
||||
let c = entry("read_file", None).derive_tool_id().unwrap();
|
||||
assert_ne!(a, b);
|
||||
|
||||
@@ -266,7 +266,7 @@ pub struct MonitorEvent {
|
||||
pub raw_text: String,
|
||||
}
|
||||
|
||||
/// Snapshot of a background task's state. Identical shape to the Grok
|
||||
/// Snapshot of a background task's state. Identical shape to the Kigi
|
||||
/// Build `TaskSnapshot` so subscribers can decode without per-source
|
||||
/// adapters.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
||||
@@ -199,7 +199,7 @@ pub fn extract_content_blocks(value: &Value) -> Vec<ContentBlock> {
|
||||
}
|
||||
|
||||
if let Some(obj) = value.as_object() {
|
||||
// grok-build `ToolRunResult`: the model sees `prompt_text` (reminders
|
||||
// kigi `ToolRunResult`: the model sees `prompt_text` (reminders
|
||||
// appended), never a JSON dump of the structured result.
|
||||
if let Some(Value::String(prompt_text)) = obj.get("prompt_text")
|
||||
&& obj.contains_key("output")
|
||||
|
||||
@@ -157,7 +157,7 @@ pub enum ToolProgress {
|
||||
/// across the wire boundary without re-encoding.
|
||||
///
|
||||
/// The `Image` variant carries optional metadata fields (`media_id`,
|
||||
/// `filename`, `path`, `metadata`) that the Grok SLOP converter uses to
|
||||
/// `filename`, `path`, `metadata`) that the Kigi SLOP converter uses to
|
||||
/// build `Media` objects with the right identifiers and file paths. Tool
|
||||
/// authors populate these in their `ToolOutput::model_output()` impls so
|
||||
/// downstream consumers don't need to know the concrete tool type.
|
||||
@@ -169,11 +169,11 @@ pub enum ContentBlock {
|
||||
Text { text: String },
|
||||
/// Image. `mime_type` is e.g. `"image/png"`; `data` is base64.
|
||||
///
|
||||
/// Optional metadata fields are used by the Grok SLOP converter to
|
||||
/// Optional metadata fields are used by the Kigi SLOP converter to
|
||||
/// produce `Media` objects with the correct identifiers:
|
||||
/// - `media_id`: unique image ID for referencing in subsequent tool calls
|
||||
/// - `filename`: human-readable filename (e.g. `"xK29f.png"`)
|
||||
/// - `path`: file path on the Grok Computer filesystem
|
||||
/// - `path`: file path on the Kigi Computer filesystem
|
||||
/// - `metadata`: arbitrary key-value pairs (e.g. `title`, `webpage_url`)
|
||||
Image {
|
||||
#[serde(alias = "mimeType")]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! Covers the JSON-erased object-safe surface (`ToolDyn`) and the
|
||||
//! variant-keyed family lookup (`ToolFamily`). Mirrors the toolbox
|
||||
//! `GrokToolDyn` / `GrokToolFamily` tests but against the runtime's
|
||||
//! `KigiToolDyn` / `KigiToolFamily` tests but against the runtime's
|
||||
//! typed `Tool` trait.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -1116,9 +1116,9 @@ mod tests {
|
||||
#[test]
|
||||
fn task_tool_input_model_parses_explicit() {
|
||||
let input: TaskToolInput =
|
||||
serde_json::from_str(r#"{"description": "d", "prompt": "p", "model": "grok-3"}"#)
|
||||
serde_json::from_str(r#"{"description": "d", "prompt": "p", "model": "kigi-3"}"#)
|
||||
.unwrap();
|
||||
assert_eq!(input.model.as_deref(), Some("grok-3"));
|
||||
assert_eq!(input.model.as_deref(), Some("kigi-3"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1142,12 +1142,12 @@ mod tests {
|
||||
#[test]
|
||||
fn sanitize_optional_arg_moves_when_no_trim() {
|
||||
assert_eq!(
|
||||
sanitize_optional_arg(Some("grok-3".into())).as_deref(),
|
||||
Some("grok-3")
|
||||
sanitize_optional_arg(Some("kigi-3".into())).as_deref(),
|
||||
Some("kigi-3")
|
||||
);
|
||||
assert_eq!(
|
||||
sanitize_optional_arg(Some(" grok-3 ".into())).as_deref(),
|
||||
Some("grok-3")
|
||||
sanitize_optional_arg(Some(" kigi-3 ".into())).as_deref(),
|
||||
Some("kigi-3")
|
||||
);
|
||||
assert!(sanitize_optional_arg(Some("null".into())).is_none());
|
||||
assert!(sanitize_optional_arg(Some(" NULL ".into())).is_none());
|
||||
@@ -1375,8 +1375,8 @@ mod tests {
|
||||
// ── Lifecycle tool descriptions ──────────────────────────────────────
|
||||
//
|
||||
// These lock the exact model-facing text. The "cli_default" cases must
|
||||
// match what the grok-shell MiniJinja templates render for the default
|
||||
// grok-build toolset (monitor + task + bash + read present, POSIX). The
|
||||
// match what the kigi-shell MiniJinja templates render for the default
|
||||
// kigi toolset (monitor + task + bash + read present, POSIX). The
|
||||
// "toolbox" cases lock the subagent-only rendering used by the backend toolbox.
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user