fix(models): deepseek-flash reasons — stop leaking its reasoning into prose

The founder saw `deepseek-flash`'s reasoning rendered as ordinary assistant
text. Cause: `model_supports_reasoning("deepseek-flash")` returned **false**, so
the client never routed `reasoning_content` to the thinking channel and it fell
through into visible output.

Why it returned false is the interesting part. That function consults the
catalog first and then falls through to a hand-maintained pile —
`starts_with("kimi-")`, `starts_with("mistral-")`, and a ~20-entry `matches!`
list. `deepseek-flash` was in no catalog, does not start with `kimi-`, and is not
in the list. It was also deliberately kept out of
`canonical_official_deepseek_model_id` so it would not inherit V4 Pro's pricing —
and that same function is what grants DeepSeek models reasoning. Keeping it out
for a pricing reason silently cost it reasoning.

Fixed at the layer that should own the fact, not with another match arm: catalog
entries in `models_dev.bundled.json` and `model_catalog.bundled.json` carrying
`supports_reasoning: true`, `context_window: 1000000`, `max_output: 384000`.
That also let the hand-added context-window arm from `b73a7060c` be **deleted** —
net less special-casing than before.

Modalities are text-only, matching the V4 Flash sibling. Secondary reports
mention native image input but the vendor notice does not state it, and claiming
vision that is not there fails requests; understating only hides a capability.
No price fields in the catalog rows: the DeepSeek family's rates are
time-varying and owned by `pricing.rs`.

Also lands the `models` extraction: `crates/tui/src/models.rs` moves to the
`codewhale-models` crate (1,837 lines, 150 consumers repointed, no shim).
`crates/tui/src` is now 955,950 lines, from 973,442 at the start of this work.

Verified by a temporary probe against the real merged catalog (removed after):
reasoning `true`, context `1_000_000`, output `384_000`, all resolved from the
catalog with no hardcoded arm.

  cargo test -p codewhale-models --locked        38 passed; 0 failed
  cargo test -p codewhale-tui --lib -- pricing:: model_catalog::
                                                 80 passed; 0 failed
  cargo test -p codewhale-config --locked        697 passed; 0 failed; 1 ignored
  cargo test -p codewhale-tui --lib -- tui::views tui::widgets commands::
                                                 1610 passed; 0 failed
  cargo check --workspace --all-targets          0 errors, 0 unused warnings

The hardcoded prefix pile still decides capability for every *other* unknown
model. That is the real defect and it is filed as #6032 for 0.9.14 — the durable
owner is the catalog plus the cloud-facts channel, which can publish a
capability for a model named an hour ago without a Rust release.

Refs: #6032, GitHub #6025.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01X57KRgwqYkmmtVQScczqiL
This commit is contained in:
CodeWhale BotandClaude Opus 5 committed 2026-09-09 23:47:28 -07:00
1 parent b73a7060ca
commit 10aa19570f
151 files changed
+856 -758

No files matched your search

Generated
+13
View File
@@ -986,6 +986,18 @@ dependencies = [
"windows",
]
[[package]]
name = "codewhale-models"
version = "0.9.13"
dependencies = [
"anyhow",
"chrono",
"codewhale-config",
"codewhale-core",
"serde",
"serde_json",
]
[[package]]
name = "codewhale-palette"
version = "0.9.13"
@@ -1117,6 +1129,7 @@ dependencies = [
"codewhale-hooks",
"codewhale-lane",
"codewhale-localization",
"codewhale-models",
"codewhale-palette",
"codewhale-paths",
"codewhale-protocol",
+1
View File
@@ -13,6 +13,7 @@ members = [
"crates/lane",
"crates/localization",
"crates/mcp",
"crates/models",
"crates/palette",
"crates/paths",
"crates/protocol",
+1 -1
View File
@@ -3,7 +3,7 @@
"about": "Offline/stale fallback Models.dev-shaped catalog snapshot for Codewhale (#3385, demoted by #4188).",
"schema": "Matches crates/config/src/models_dev.rs ModelsDevCatalog ({ models, providers }).",
"role": "NOT a competing source of truth. Preferred metadata is the live Models.dev catalog published into ProviderLake (#4187). This asset is used only when live/cache rows are unavailable (offline startup, failed refresh, or empty cache).",
"source": "Compact offline seed of verified in-repo defaults (context/output from crates/tui/src/models.rs; USD pricing from crates/tui/src/pricing.rs) for providers Codewhale ships with. It is intentionally smaller than a full Models.dev dump; live refresh supersedes these rows on (provider, wire_model_id) identity.",
"source": "Compact offline seed of verified in-repo defaults (context/output from crates/models/src/lib.rs; USD pricing from crates/tui/src/pricing.rs) for providers Codewhale ships with. It is intentionally smaller than a full Models.dev dump; live refresh supersedes these rows on (provider, wire_model_id) identity.",
"honesty": "Pricing is intentionally OMITTED where a flat catalog row would be misleading: DeepSeek-native rows (priced via the time-aware DeepSeek table elsewhere, kept UnknownOrStale at the route layer), Grok 4.6 (rates double once a prompt reaches 200K tokens), aggregator-hosted DeepSeek rows (aggregator account terms, not DeepSeek Platform pricing), Xiaomi MiMo rows (published PAYG rates apply only to sk- pay-as-you-go keys; the catalog cannot distinguish that billing surface from credit/quota Token Plan keys, so MiMo stays unpriced), and Alibaba Model Studio Token/Coding Plan rows (upstream lists zero per-token cost because usage draws on plan quota, not per-token billing; a zero here would read as 'free'). Absent pricing surfaces as PricingSku::UnknownOrStale, never a fabricated zero.",
"default_rows": "Each provider's `default: true` wire id equals that provider's built-in DEFAULT_*_MODEL so RouteResolver::new() and the descriptor stay in agreement when offline.",
"curated": "qwen3.8-max (GA) is curated ahead of upstream Models.dev, which as of 2026-08-03 lists only qwen3.8-max-preview; facts verified against the owner's Token Plan console (2026-08-03): ~1M context, 128K output, image understanding, always-on reasoning. deepseek-v4-flash-0731 keeps the console/in-repo wire id for the row upstream serves as deepseek-v4-flash. Coding Plan rows for qwen3.8-max-preview, deepseek-v4-pro, deepseek-v4-flash-0731, and glm-5.2 are curated from the Token Plan upstream entries (upstream alibaba-coding-plan does not list them yet); the in-repo route layer already offers the same model set on both plans. Upstream provider ids alibaba-token-plan(-cn) / alibaba-coding-plan(-cn) were merged onto the CodeWhale provider ids (live refresh normalizes them via ProviderKind aliases; the -cn regional variants stay upstream-id browse rows until Codewhale ships China endpoints).",
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "codewhale-models"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
description = "Provider request/response models and the offline model metadata catalog for Codewhale."
[features]
# Exposes the catalog test hooks (`test_catalog_lock`,
# `replace_active_catalog_for_test`) that used to be reachable as `#[cfg(test)]`
# items inside `codewhale-tui`. Enabled only from dev-dependencies.
test-support = []
[lints]
workspace = true
[dependencies]
anyhow.workspace = true
chrono.workspace = true
codewhale-config = { path = "../config", version = "0.9.13" }
codewhale-core = { path = "../core", version = "0.9.13" }
serde.workspace = true
serde_json = { workspace = true, features = ["preserve_order"] }
@@ -101,6 +101,19 @@
],
"supports_reasoning": true
},
"deepseek-flash": {
"context_window": 1000000,
"id": "deepseek-flash",
"max_output": 384000,
"modalities": [
"text"
],
"provenance": "bundled",
"supported_parameters": [
"reasoning"
],
"supports_reasoning": true
},
"deepseek-v4-flash-vision-exp": {
"context_window": 1000000,
"id": "deepseek-v4-flash-vision-exp",
@@ -2,6 +2,8 @@
use serde::{Deserialize, Serialize};
pub mod model_catalog;
/// Context window used only for legacy DeepSeek model IDs that do not name a
/// newer V4 alias and do not carry an explicit `*k` suffix.
pub const LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS: u32 = 128_000;
@@ -36,7 +38,7 @@ pub const DIRECT_KIMI_K3_MAX_OUTPUT_TOKENS: u32 = 1_048_576;
/// models resolve to their own scaled value via
/// `compaction_threshold_for_model` (#664).
pub const DEFAULT_COMPACTION_TOKEN_THRESHOLD: usize = 102_400;
pub(crate) fn canonical_official_deepseek_model_id(model: &str) -> Option<&'static str> {
pub fn canonical_official_deepseek_model_id(model: &str) -> Option<&'static str> {
match model.trim().to_ascii_lowercase().as_str() {
"deepseek-v4-pro"
| "deepseek-v4pro"
@@ -54,7 +56,7 @@ pub(crate) fn canonical_official_deepseek_model_id(model: &str) -> Option<&'stat
}
}
#[cfg(test)]
#[cfg(any(test, feature = "test-support"))]
const COMPACTION_THRESHOLD_PERCENT: u32 = 80;
// === Core Message Types ===
@@ -109,7 +111,7 @@ pub struct MessageResponse {
/// True when the provider ended generation because its output allowance was
/// exhausted. Providers use several wire spellings for the same condition.
#[must_use]
pub(crate) fn is_output_limit_stop_reason(reason: Option<&str>) -> bool {
pub fn is_output_limit_stop_reason(reason: Option<&str>) -> bool {
reason.is_some_and(|reason| {
let reason = reason
.trim()
@@ -126,7 +128,7 @@ pub(crate) fn is_output_limit_stop_reason(reason: Option<&str>) -> bool {
/// response. Responses API reasons carry an `incomplete:` prefix so unknown
/// future reasons cannot accidentally be accepted as a finished answer.
#[must_use]
pub(crate) fn is_incomplete_stop_reason(reason: Option<&str>) -> bool {
pub fn is_incomplete_stop_reason(reason: Option<&str>) -> bool {
is_output_limit_stop_reason(reason)
|| reason.is_some_and(|reason| {
let reason = reason.trim().to_ascii_lowercase();
@@ -139,7 +141,7 @@ pub(crate) fn is_incomplete_stop_reason(reason: Option<&str>) -> bool {
}
#[must_use]
pub(crate) fn stop_reason_detail(reason: Option<&str>) -> &str {
pub fn stop_reason_detail(reason: Option<&str>) -> &str {
reason
.map(str::trim)
.and_then(|reason| reason.strip_prefix("incomplete:").or(Some(reason)))
@@ -570,7 +572,7 @@ pub fn effective_muse_wire_id(model: &str) -> &str {
}
#[must_use]
pub(crate) fn model_is_openai_reasoning_family(model: &str) -> bool {
pub fn model_is_openai_reasoning_family(model: &str) -> bool {
let lower = model.to_lowercase();
is_openai_gpt_55_api_model(&lower)
|| is_openai_gpt_56_api_model(&lower)
@@ -583,7 +585,7 @@ fn is_openai_gpt_55_api_model(model_lower: &str) -> bool {
|| has_date_snapshot_suffix(model_lower, "gpt-5.5-pro-")
}
pub(crate) fn is_openai_gpt_56_api_model(model_lower: &str) -> bool {
pub fn is_openai_gpt_56_api_model(model_lower: &str) -> bool {
matches!(
model_lower,
"gpt-5.6" | "gpt-5.6-sol" | "gpt-5.6-terra" | "gpt-5.6-luna"
@@ -608,7 +610,7 @@ fn is_openai_codex_model(model_lower: &str) -> bool {
)
}
pub(crate) fn has_date_snapshot_suffix(model_lower: &str, prefix: &str) -> bool {
pub fn has_date_snapshot_suffix(model_lower: &str, prefix: &str) -> bool {
let Some(rest) = model_lower.strip_prefix(prefix) else {
return false;
};
@@ -631,7 +633,7 @@ pub(crate) fn has_date_snapshot_suffix(model_lower: &str, prefix: &str) -> bool
/// a fact about the route, and every surface that shows such a window must
/// mark it unverified.
#[must_use]
pub(crate) fn name_suffix_context_window_hint(model: &str) -> Option<u32> {
pub fn name_suffix_context_window_hint(model: &str) -> Option<u32> {
if crate::model_catalog::resolved_context_window(model).is_some() {
return None;
}
@@ -674,7 +676,7 @@ fn explicit_context_window_hint(model_lower: &str) -> Option<u32> {
/// Derive a compaction token threshold from model context and a caller-supplied
/// percentage.
#[must_use]
#[cfg(test)]
#[cfg(any(test, feature = "test-support"))]
pub fn compaction_threshold_for_model_at_percent(model: &str, percent: f64) -> usize {
let Some(window) = context_window_for_model(model) else {
return DEFAULT_COMPACTION_TOKEN_THRESHOLD;
@@ -75,7 +75,7 @@ impl CatalogCache {
}
#[derive(Debug, Clone)]
pub(crate) struct MergedCatalog {
pub struct MergedCatalog {
user_overrides: BTreeMap<String, CatalogEntry>,
provider_cache: Option<CatalogCache>,
bundled: CatalogCache,
@@ -83,7 +83,7 @@ pub(crate) struct MergedCatalog {
}
impl MergedCatalog {
pub(crate) fn from_sources(
pub fn from_sources(
user_overrides: BTreeMap<String, CatalogEntry>,
provider_cache: Option<CatalogCache>,
bundled: CatalogCache,
@@ -197,21 +197,21 @@ pub fn load_cached() -> Option<CatalogCache> {
serde_json::from_str(&raw).ok()
}
#[cfg(test)]
#[cfg(any(test, feature = "test-support"))]
static TEST_CATALOG_LOCK: std::sync::LazyLock<std::sync::Mutex<()>> =
std::sync::LazyLock::new(|| std::sync::Mutex::new(()));
#[cfg(test)]
pub(crate) fn test_catalog_lock() -> std::sync::MutexGuard<'static, ()> {
#[cfg(any(test, feature = "test-support"))]
pub fn test_catalog_lock() -> std::sync::MutexGuard<'static, ()> {
TEST_CATALOG_LOCK.lock().expect("model catalog test lock")
}
#[cfg(test)]
pub(crate) struct ActiveCatalogGuard {
#[cfg(any(test, feature = "test-support"))]
pub struct ActiveCatalogGuard {
previous: MergedCatalog,
}
#[cfg(test)]
#[cfg(any(test, feature = "test-support"))]
impl Drop for ActiveCatalogGuard {
fn drop(&mut self) {
let mut active = active_catalog().write().expect("active catalog write lock");
@@ -219,8 +219,8 @@ impl Drop for ActiveCatalogGuard {
}
}
#[cfg(test)]
pub(crate) fn replace_active_catalog_for_test(catalog: MergedCatalog) -> ActiveCatalogGuard {
#[cfg(any(test, feature = "test-support"))]
pub fn replace_active_catalog_for_test(catalog: MergedCatalog) -> ActiveCatalogGuard {
let mut active = active_catalog().write().expect("active catalog write lock");
let previous = active.clone();
*active = catalog;
+4
View File
@@ -39,6 +39,7 @@ codewhale-core = { path = "../core", version = "0.9.13" }
codewhale-execpolicy = { path = "../execpolicy", version = "0.9.13" }
codewhale-hooks = { path = "../hooks", version = "0.9.13" }
codewhale-lane = { path = "../lane", version = "0.9.13" }
codewhale-models = { path = "../models", version = "0.9.13" }
codewhale-palette = { path = "../palette", version = "0.9.13" }
codewhale-paths = { path = "../paths", version = "0.9.13" }
codewhale-protocol = { path = "../protocol", version = "0.9.13" }
@@ -120,6 +121,9 @@ rusty_alloc-api = { workspace = true, optional = true }
codewhale-build-support = { path = "../build-support", version = "0.9.13" }
[dev-dependencies]
codewhale-models = { path = "../models", version = "0.9.13", features = [
"test-support",
] }
cucumber = "0.23.0"
jsonschema.workspace = true
wiremock = "0.6"
+1 -5
View File
@@ -1,12 +1,8 @@
mod models {
pub use codewhale_core::request::{ContentBlock, Message};
pub use codewhale_core::role::Role;
}
#[path = "../src/session_tree.rs"]
#[allow(dead_code)] // The probe intentionally exercises only the journal hot paths.
mod session_tree;
use models::Message;
use codewhale_models::Message;
use serde::{Deserialize, Serialize};
use session_tree::SessionJournal;
use std::io::Write;
+4 -4
View File
@@ -51,15 +51,15 @@ use crate::core::engine::{
exec_shell_ask_rule_decision_for_policy, file_tool_ask_rule_decision_for_policy,
};
use crate::llm_client::{LlmClient, StreamEventBox};
use crate::models::Role;
use crate::models::{
ContentBlock, ContentBlockStart, Delta, Message, MessageRequest, StreamEvent, SystemPrompt,
};
use crate::tools::spec::{ApprovalRequirement, PreparedToolCall, RichToolResult, ToolError};
use crate::tools::{ToolContext, ToolRegistry, ToolRegistryBuilder};
use crate::worker_profile::ShellPolicy;
use codewhale_config::AppMode;
use codewhale_execpolicy::ApprovalMode;
use codewhale_models::Role;
use codewhale_models::{
ContentBlock, ContentBlockStart, Delta, Message, MessageRequest, StreamEvent, SystemPrompt,
};
const ACP_PROTOCOL_VERSION: u64 = 1;
+16 -14
View File
@@ -41,8 +41,10 @@ use crate::llm_client::{
mod catalog_tests;
use crate::logging;
use crate::models::Role;
use crate::models::{ContentBlock, Message, MessageRequest, MessageResponse, SystemPrompt, Usage};
use codewhale_models::Role;
use codewhale_models::{
ContentBlock, Message, MessageRequest, MessageResponse, SystemPrompt, Usage,
};
/// Every provider request that can feed the interactive TUI's attached CWC run
/// takes a shared permit at this lowest common dispatch seam. Runtime Chat holds
@@ -2701,7 +2703,7 @@ impl DeepSeekClient {
omitted_tool_count: usize,
) -> crate::llm_client::StreamEventBox {
Box::pin(async_stream::stream! {
yield Ok(crate::models::StreamEvent::ToolProjectionWarning {
yield Ok(codewhale_models::StreamEvent::ToolProjectionWarning {
provider,
omitted_tool_names,
omitted_tool_count,
@@ -2763,10 +2765,10 @@ impl DeepSeekClient {
};
let usage = (response.usage != Usage::default()).then_some(response.usage.clone());
let translated =
if crate::models::is_incomplete_stop_reason(response.stop_reason.as_deref()) {
if codewhale_models::is_incomplete_stop_reason(response.stop_reason.as_deref()) {
Err(anyhow::anyhow!(
"translate: provider response incomplete ({})",
crate::models::stop_reason_detail(response.stop_reason.as_deref())
codewhale_models::stop_reason_detail(response.stop_reason.as_deref())
))
} else {
translation_text_from_response(&response)
@@ -2837,10 +2839,10 @@ impl DeepSeekClient {
let usage = parse_usage(value.get("usage"));
let stop_reason = value["choices"][0]["finish_reason"].as_str();
let usage = (usage_reported && usage != Usage::default()).then_some(usage);
let translated = if crate::models::is_incomplete_stop_reason(stop_reason) {
let translated = if codewhale_models::is_incomplete_stop_reason(stop_reason) {
Err(anyhow::anyhow!(
"translate: provider response incomplete ({})",
crate::models::stop_reason_detail(stop_reason)
codewhale_models::stop_reason_detail(stop_reason)
))
} else {
value["choices"][0]["message"]["content"]
@@ -5032,7 +5034,7 @@ pub(crate) fn responses_tool_output_for_test(
}
#[cfg(test)]
pub(crate) fn chat_messages_for_test(messages: &[crate::models::Message]) -> Vec<Value> {
pub(crate) fn chat_messages_for_test(messages: &[codewhale_models::Message]) -> Vec<Value> {
chat::build_chat_messages(None, messages, "gpt-4o")
}
@@ -5074,13 +5076,13 @@ mod tests {
DEFAULT_CONCENTRATE_BASE_URL, DEFAULT_CONCENTRATE_MODEL, DEFAULT_EDENAI_MODEL,
DEFAULT_TELECOMJS_MODEL, OPENROUTER_QWEN_3_6_FLASH_MODEL, ProviderConfig, ProvidersConfig,
};
use crate::models::{
ContentBlock, ContentBlockStart, Delta, Message, MessageRequest, MessageResponse,
StreamEvent, Tool,
};
use crate::tools::apply_patch::ApplyPatchTool;
use crate::tools::spec::ToolSpec;
use crate::tools::{ToolContext, ToolRegistryBuilder};
use codewhale_models::{
ContentBlock, ContentBlockStart, Delta, Message, MessageRequest, MessageResponse,
StreamEvent, Tool,
};
use codewhale_protocol::runtime::DynamicToolSpec;
use serde_json::json;
use wiremock::matchers::{header, method, path};
@@ -7286,7 +7288,7 @@ mod tests {
.expect("projection warning precedes provider SSE")
.expect("projection warning is not a stream error");
let (provider, omitted_tool_names, omitted_tool_count) = match first {
crate::models::StreamEvent::ToolProjectionWarning {
codewhale_models::StreamEvent::ToolProjectionWarning {
provider,
omitted_tool_names,
omitted_tool_count,
@@ -7301,7 +7303,7 @@ mod tests {
while let Some(event) = stream.next().await {
if matches!(
event.expect("captured SSE response remains valid"),
crate::models::StreamEvent::ToolProjectionWarning { .. }
codewhale_models::StreamEvent::ToolProjectionWarning { .. }
) {
additional_warnings += 1;
}
+13 -13
View File
@@ -17,7 +17,7 @@
//! non-cached `input_tokens`, and the normalized `input_tokens` is the sum
//! of all three (total prompt, the DeepSeek convention);
//! - signed-thinking handling: `signature_delta` is captured into
//! [`crate::models::Delta::SignatureDelta`] and assistant thinking blocks
//! [`codewhale_models::Delta::SignatureDelta`] and assistant thinking blocks
//! replay verbatim (signature included); unsigned thinking blocks are
//! dropped from replay because the API rejects them.
//!
@@ -30,8 +30,8 @@ use serde_json::{Value, json};
use crate::config::{ApiProvider, wire_model_for_provider_route};
use crate::llm_client::StreamEventBox;
use crate::logging;
use crate::models::{ContentBlock, MessageRequest, MessageResponse, StreamEvent, Usage};
use crate::tools::schema_sanitize;
use codewhale_models::{ContentBlock, MessageRequest, MessageResponse, StreamEvent, Usage};
use super::prepared::WireDialect;
use super::role_placement::{RolePlacement, role_placement};
@@ -54,8 +54,8 @@ impl DeepSeekClient {
if let Some(system) = request.system.as_ref() {
body["system"] = match system {
crate::models::SystemPrompt::Text(text) => json!(text),
crate::models::SystemPrompt::Blocks(blocks) => json!(
codewhale_models::SystemPrompt::Text(text) => json!(text),
codewhale_models::SystemPrompt::Blocks(blocks) => json!(
blocks
.iter()
.map(|block| {
@@ -128,7 +128,7 @@ impl DeepSeekClient {
// gateways (#4978, e.g. Sensenova) only accept the documented
// enabled/disabled/auto thinking types, so non-native routes get the
// portable `{"type":"enabled","budget_tokens":N}` shape instead.
let thinking_capable = crate::models::model_supports_reasoning(&model);
let thinking_capable = codewhale_models::model_supports_reasoning(&model);
let is_minimax_provider = self.api_provider == ApiProvider::MinimaxAnthropic;
let is_minimax = crate::config::is_exact_minimax_anthropic_m3_route(
self.api_provider,
@@ -558,7 +558,7 @@ fn anthropic_tool_choice(tool_choice: &Value) -> Value {
/// `system` message ended up on the wire for the provider to 400 on. It now
/// comes from the shared placement table, and pairs the table rejects are
/// refused at the outbound seam before this function ever runs.
pub(super) fn message_to_anthropic(message: &crate::models::Message) -> Option<Value> {
pub(super) fn message_to_anthropic(message: &codewhale_models::Message) -> Option<Value> {
let placement = role_placement(&message.role, WireDialect::AnthropicMessages);
let wire_role = match placement {
RolePlacement::User | RolePlacement::Developer => "user",
@@ -588,7 +588,7 @@ pub(super) fn message_to_anthropic(message: &crate::models::Message) -> Option<V
.to_string();
text["text"] = json!(format!(
"{}{}",
crate::models::INTERRUPTED_ASSISTANT_CONTEXT_PREFIX,
codewhale_models::INTERRUPTED_ASSISTANT_CONTEXT_PREFIX,
existing
));
}
@@ -896,8 +896,8 @@ fn anthropic_error_fields(error: &Value) -> (String, String) {
#[cfg(test)]
mod tests {
use super::*;
use crate::models::Role;
use crate::models::{CacheControl, Message, SystemBlock, SystemPrompt, Tool};
use codewhale_models::Role;
use codewhale_models::{CacheControl, Message, SystemBlock, SystemPrompt, Tool};
fn request_with(
model: &str,
@@ -1607,7 +1607,7 @@ mod tests {
#[test]
fn sse_fixture_decodes_text_thinking_signature_and_tool_use() {
use crate::models::{ContentBlockStart, Delta};
use codewhale_models::{ContentBlockStart, Delta};
let events = [
r#"{"type":"message_start","message":{"id":"msg_01","type":"message","role":"assistant","content":[],"model":"claude-sonnet-4-6","stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":2045,"cache_read_input_tokens":18000,"output_tokens":1}}}"#,
@@ -1754,7 +1754,7 @@ mod tests {
// whole reason the projection exists; if it regresses, every locally
// attached screenshot 400s on the native route.
let block = content_block_to_anthropic(&ContentBlock::ImageUrl {
image_url: crate::models::ImageUrlContent {
image_url: codewhale_models::ImageUrlContent {
url: "data:image/png;base64,QUJD".to_string(),
},
})
@@ -1795,7 +1795,7 @@ mod tests {
#[test]
fn remote_image_url_stays_a_url_source() {
let block = content_block_to_anthropic(&ContentBlock::ImageUrl {
image_url: crate::models::ImageUrlContent {
image_url: codewhale_models::ImageUrlContent {
url: "https://example.com/shot.png".to_string(),
},
})
@@ -1814,7 +1814,7 @@ mod tests {
"data:image/png,QUJD",
] {
let block = content_block_to_anthropic(&ContentBlock::ImageUrl {
image_url: crate::models::ImageUrlContent {
image_url: codewhale_models::ImageUrlContent {
url: url.to_string(),
},
})
+14 -11
View File
@@ -40,7 +40,7 @@ use crate::config::ApiProvider;
use crate::llm_client::StreamEventBox;
use crate::llm_client::sanitize_http_error_body;
use crate::logging;
use crate::models::{
use codewhale_models::{
ContentBlock, ContentBlockStart, Delta, Message, MessageDelta, MessageRequest, MessageResponse,
StreamEvent, SystemPrompt, Tool, ToolCaller, Usage, is_openai_gpt_56_api_model,
model_is_openai_reasoning_family, model_supports_reasoning,
@@ -55,7 +55,7 @@ use super::{
apply_reasoning_effort, bounded_error_text, from_api_tool_name, parse_usage,
release_stream_buffer, system_to_instructions, to_api_tool_name,
};
use crate::models::Role;
use codewhale_models::Role;
fn apply_provider_token_limit(
body: &mut Value,
@@ -1079,7 +1079,7 @@ pub(crate) fn build_chat_wire_body(
build_chat_messages_for_request_and_provider_and_route(request, provider, base_url);
let model = {
let wire = wire_model_for_provider_route(provider, base_url, &request.model);
crate::models::effective_muse_wire_id(&wire).to_string()
codewhale_models::effective_muse_wire_id(&wire).to_string()
};
validate_google_thought_signature_replay(base_url, &model, &messages)?;
let mut body = if stream {
@@ -2693,7 +2693,7 @@ fn build_chat_messages_with_reasoning(
let content = if placement == RolePlacement::InterruptedAssistant {
format!(
"{}{}",
crate::models::INTERRUPTED_ASSISTANT_CONTEXT_PREFIX,
codewhale_models::INTERRUPTED_ASSISTANT_CONTEXT_PREFIX,
text_parts.join("\n")
)
} else {
@@ -4440,7 +4440,7 @@ mod stream_diagnostics_tests {
mod arcee_waf_message_encoding_tests {
use super::build_chat_messages_for_request_and_provider;
use crate::config::ApiProvider;
use crate::models::{MessageRequest, SystemPrompt};
use codewhale_models::{MessageRequest, SystemPrompt};
use serde_json::Value;
fn request_with_system(system: &str) -> MessageRequest {
@@ -4523,8 +4523,8 @@ mod minimax_reasoning_replay_tests {
ApiProvider, DEFAULT_KIMI_CODE_BASE_URL, DEFAULT_MINIMAX_MODEL,
DEFAULT_MODELSTUDIO_TOKEN_PLAN_BASE_URL, DEFAULT_MOONSHOT_BASE_URL, KIMI_CODE_K3_MODEL,
};
use crate::models::Role;
use crate::models::{ContentBlock, Message, MessageRequest};
use codewhale_models::Role;
use codewhale_models::{ContentBlock, Message, MessageRequest};
fn request_with_assistant_thinking() -> MessageRequest {
MessageRequest {
@@ -6229,8 +6229,8 @@ mod image_block_wire_tests {
//! message whose `content` is an array of parts, with the image as
//! `{"type":"image_url","image_url":{"url":…}}`.
use super::{ApiProvider, build_chat_messages, build_chat_wire_body};
use crate::models::Role;
use crate::models::{ContentBlock, ImageUrlContent, Message, MessageRequest};
use codewhale_models::Role;
use codewhale_models::{ContentBlock, ImageUrlContent, Message, MessageRequest};
const DATA_URL: &str = "data:image/png;base64,QUJD";
@@ -7116,10 +7116,13 @@ mod mistral_reasoning_tests {
"mistral-small-latest",
"magistral-small-latest",
] {
assert!(crate::models::model_supports_reasoning(model), "{model}");
assert!(codewhale_models::model_supports_reasoning(model), "{model}");
}
for model in ["mistral-code-latest", "mistral-large-latest"] {
assert!(!crate::models::model_supports_reasoning(model), "{model}");
assert!(
!codewhale_models::model_supports_reasoning(model),
"{model}"
);
}
}
}
@@ -5,7 +5,7 @@
//! directly to verify each "class of stream failure" the engine relies on.
use super::*;
use crate::client::wire::{InvalidSseUtf8, SseLineDecoder};
use crate::models::{ContentBlockStart, Delta, StreamEvent};
use codewhale_models::{ContentBlockStart, Delta, StreamEvent};
/// Decode a raw SSE-data JSON chunk into our internal events, mirroring
/// the per-event call shape used by `handle_chat_completion_stream`.
+2 -2
View File
@@ -1,9 +1,9 @@
use super::DeepSeekClient;
use super::chat::{parse_chat_message, parse_sse_chunk};
use crate::config::{Config, ProviderConfig, ProvidersConfig};
use crate::models::Role;
use crate::models::{ContentBlock, Delta, Message, MessageRequest, StreamEvent, Tool};
use anyhow::Result;
use codewhale_models::Role;
use codewhale_models::{ContentBlock, Delta, Message, MessageRequest, StreamEvent, Tool};
use serde_json::{Value, json};
fn ds4_client() -> DeepSeekClient {
+2 -2
View File
@@ -1235,8 +1235,8 @@ mod tests {
mod dialect_seam_tests {
use super::*;
use crate::config::{Config, ProviderConfig, ProvidersConfig};
use crate::models::Role;
use crate::models::{ContentBlock, Message, MessageRequest, SystemPrompt, Tool};
use codewhale_models::Role;
use codewhale_models::{ContentBlock, Message, MessageRequest, SystemPrompt, Tool};
use serde_json::json;
use super::super::DeepSeekClient;
+3 -3
View File
@@ -13,11 +13,11 @@ use serde_json::{Value, json};
use crate::config::ApiProvider;
use crate::llm_client::StreamEventBox;
use crate::logging;
use crate::models::{
use crate::tools::schema_sanitize;
use codewhale_models::{
ContentBlock, ContentBlockStart, Delta, MessageDelta, MessageRequest, MessageResponse,
OpaqueReasoningState, StreamEvent, Tool, Usage,
};
use crate::tools::schema_sanitize;
use super::prepared::WireDialect;
use super::role_placement::{RolePlacement, role_placement};
@@ -776,7 +776,7 @@ pub(super) fn convert_messages_to_responses_input(
let text = if placement == RolePlacement::InterruptedAssistant {
format!(
"{}{}",
crate::models::INTERRUPTED_ASSISTANT_CONTEXT_PREFIX,
codewhale_models::INTERRUPTED_ASSISTANT_CONTEXT_PREFIX,
text
)
} else {
+4 -4
View File
@@ -6,9 +6,9 @@ use std::sync::atomic::{AtomicUsize, Ordering};
use futures_util::StreamExt;
use crate::config::{Config, ProviderConfig, ProvidersConfig, RetryConfig};
use crate::models::Message;
use crate::models::Role;
use crate::models::SystemPrompt;
use codewhale_models::Message;
use codewhale_models::Role;
use codewhale_models::SystemPrompt;
use wiremock::matchers::{method, path};
use wiremock::{Mock, MockServer, Request, Respond, ResponseTemplate};
@@ -1339,7 +1339,7 @@ fn user_image_becomes_an_input_image_item() {
let mut request = minimal_responses_request();
request.messages[0].content.push(ContentBlock::ImageUrl {
image_url: crate::models::ImageUrlContent {
image_url: codewhale_models::ImageUrlContent {
url: DATA_URL.to_string(),
},
});
+3 -3
View File
@@ -27,7 +27,7 @@
//! bytes, never how much the model should trust them.
use super::prepared::WireDialect;
use crate::models::{Message, Role};
use codewhale_models::{Message, Role};
/// Which channel of a wire body a message renders into.
///
@@ -147,7 +147,7 @@ pub(crate) fn reject_unsupported_roles(
#[cfg(test)]
mod tests {
use super::{RolePlacement, WireDialect, reject_unsupported_roles, role_placement};
use crate::models::{ContentBlock, Message, Role};
use codewhale_models::{ContentBlock, Message, Role};
const DIALECTS: [WireDialect; 3] = [
WireDialect::ChatCompletions,
@@ -286,7 +286,7 @@ mod adapter_agreement_tests {
use super::super::{anthropic, chat, responses};
use crate::config::ApiProvider;
use crate::models::{
use codewhale_models::{
ContentBlock, INTERRUPTED_ASSISTANT_CONTEXT_PREFIX, Message, MessageRequest, Role,
};
+2 -2
View File
@@ -6,7 +6,7 @@
use anyhow::Result;
use serde_json::Value;
use crate::models::{ServerToolUsage, Usage};
use codewhale_models::{ServerToolUsage, Usage};
pub(crate) fn saturating_u32(value: u64) -> u32 {
u32::try_from(value).unwrap_or(u32::MAX)
@@ -392,7 +392,7 @@ mod tests {
// Invariant 2: pricing does not add reasoning a second time. The same
// usage with the reasoning field removed must cost the same.
let without = crate::models::Usage {
let without = codewhale_models::Usage {
reasoning_tokens: None,
..usage.clone()
};
+9 -9
View File
@@ -387,7 +387,7 @@ impl CommandSessionLifecycleContext for SessionLifecycleAdapter<'_> {
.content
.iter()
.filter_map(|b| match b {
crate::models::ContentBlock::Text { text, .. } => Some(text.as_str()),
codewhale_models::ContentBlock::Text { text, .. } => Some(text.as_str()),
_ => None,
})
.collect::<Vec<_>>()
@@ -627,7 +627,7 @@ impl CommandSessionLifecycleContext for SessionLifecycleAdapter<'_> {
source_session
.system_prompt
.as_ref()
.map(|s| crate::models::SystemPrompt::Text(s.clone()))
.map(|s| codewhale_models::SystemPrompt::Text(s.clone()))
.as_ref(),
source_session.metadata.mode.as_deref(),
);
@@ -671,7 +671,7 @@ impl CommandSessionLifecycleContext for SessionLifecycleAdapter<'_> {
system_prompt: forked
.system_prompt
.as_ref()
.map(|s| crate::models::SystemPrompt::Text(s.clone())),
.map(|s| codewhale_models::SystemPrompt::Text(s.clone())),
model: forked.metadata.model.clone(),
workspace: app.workspace.clone(),
mode,
@@ -4043,8 +4043,8 @@ impl App {
#[cfg(test)]
mod tests {
use super::*;
use crate::models::Role;
use codewhale_localization::Locale;
use codewhale_models::Role;
use tempfile::TempDir;
fn test_app() -> App {
@@ -4711,9 +4711,9 @@ mod tests {
app.session.total_conversation_tokens = 2_000;
app.goal_continuation_waiting = true;
app.is_loading = false;
app.api_messages.push(crate::models::Message {
role: crate::models::Role::User,
content: vec![crate::models::ContentBlock::Text {
app.api_messages.push(codewhale_models::Message {
role: codewhale_models::Role::User,
content: vec![codewhale_models::ContentBlock::Text {
text: "work".to_string(),
cache_control: None,
}],
@@ -5604,8 +5604,8 @@ mod tests {
fn user_message(text: &str) -> Message {
Message {
role: crate::models::Role::User,
content: vec![crate::models::ContentBlock::Text {
role: codewhale_models::Role::User,
content: vec![codewhale_models::ContentBlock::Text {
text: text.to_string(),
cache_control: None,
}],
@@ -518,17 +518,17 @@ fn localized(locale: Locale, id: MessageId, replacements: &[(&str, &str)]) -> St
#[cfg(test)]
mod tests {
use crate::models::Role;
use codewhale_models::Role;
use std::path::PathBuf;
use tempfile::TempDir;
use super::*;
use crate::config::{ApiProvider, Config};
use crate::models::{ContentBlock, Message};
use crate::tui::app::TuiOptions;
use crate::tui::history::HistoryCell;
use codewhale_config::AppMode;
use codewhale_models::{ContentBlock, Message};
#[test]
fn status_keeps_current_session_snapshot_remedy_after_notice_delivery() {
+1 -1
View File
@@ -72,10 +72,10 @@ fn execute_copy(app: &mut App) -> CommandResult {
mod tests {
use super::*;
use crate::config::Config;
use crate::models::{ContentBlock, Message, Role};
use crate::tui::app::TuiOptions;
use crate::tui::clipboard::ClipboardHandler;
use crate::tui::history::{HistoryCell, history_cells_from_message};
use codewhale_models::{ContentBlock, Message, Role};
use std::path::{Path, PathBuf};
use tempfile::TempDir;
+2 -2
View File
@@ -753,11 +753,11 @@ mod tests {
use super::*;
use crate::client::PromptInspection;
use crate::config::Config;
use crate::models::Message;
use crate::models::Role;
use crate::tui::app::{App, TuiOptions, TurnCacheRecord};
use crate::tui::history::HistoryCell;
use codewhale_config::AppMode;
use codewhale_models::Message;
use codewhale_models::Role;
use std::ffi::OsString;
use std::path::PathBuf;
use std::time::Instant;
+14 -14
View File
@@ -10,13 +10,13 @@
//! `/workflow` alone.
use crate::commands::traits::{CommandInfo, RegisterCommand};
use crate::models::ContentBlock;
use crate::tui::app::WORKFLOW_DRAFT_INSTRUCTION_PREFIX;
use crate::tui::app::{App, AppAction};
use codewhale_config::AppMode;
#[cfg(test)]
use codewhale_execpolicy::ApprovalMode;
use codewhale_localization::MessageId;
use codewhale_models::ContentBlock;
use super::CommandResult;
@@ -132,7 +132,7 @@ fn envelope_from_instruction(prefix: &str, instruction: &str) -> Option<Workflow
serde_json::from_str(encoded).ok()
}
fn user_instruction(message: &crate::models::Message) -> Option<&str> {
fn user_instruction(message: &codewhale_models::Message) -> Option<&str> {
if message.role != "user" {
return None;
}
@@ -505,7 +505,7 @@ pub fn auto(app: &mut App, arg: Option<&str>) -> CommandResult {
#[cfg(test)]
mod tests {
use super::*;
use crate::models::Role;
use codewhale_models::Role;
use std::path::PathBuf;
use crate::tui::app::TuiOptions;
@@ -596,14 +596,14 @@ mod tests {
assert_eq!(objective.chars().count(), WORKFLOW_OBJECTIVE_MAX_CHARS);
assert!(objective.ends_with('…'));
app.api_messages.push(crate::models::Message {
app.api_messages.push(codewhale_models::Message {
role: Role::User,
content: vec![ContentBlock::Text {
text: instruction,
cache_control: None,
}],
});
app.api_messages.push(crate::models::Message {
app.api_messages.push(codewhale_models::Message {
role: Role::Assistant,
content: vec![ContentBlock::Text {
text: "Reviewed bounded objective and proposed phases.".to_string(),
@@ -637,14 +637,14 @@ mod tests {
else {
panic!("expected WorkflowInstruction action");
};
app.api_messages.push(crate::models::Message {
app.api_messages.push(codewhale_models::Message {
role: Role::User,
content: vec![ContentBlock::Text {
text: format!("{instruction}\n\n---\n\nUser request: {display}"),
cache_control: None,
}],
});
app.api_messages.push(crate::models::Message {
app.api_messages.push(codewhale_models::Message {
role: Role::Assistant,
content: vec![ContentBlock::Text {
text: "Objective, phases, workers, and risks. Run /workflow confirm to start."
@@ -657,7 +657,7 @@ mod tests {
// explicit confirm must still find the reviewed draft (regression: the
// old supersede rule cancelled the draft on any ordinary message and
// made repeated confirm attempts impossible).
app.api_messages.push(crate::models::Message {
app.api_messages.push(codewhale_models::Message {
role: Role::User,
content: vec![ContentBlock::Text {
text: "hmm it won't let me confirm it lol".to_string(),
@@ -680,7 +680,7 @@ mod tests {
else {
panic!("expected WorkflowInstruction action");
};
app.api_messages.push(crate::models::Message {
app.api_messages.push(codewhale_models::Message {
role: Role::User,
content: vec![ContentBlock::Text {
text: format!("{redraft_instruction}\n\n---\n\nUser request: {redraft_display}"),
@@ -711,7 +711,7 @@ mod tests {
else {
panic!("expected WorkflowInstruction action");
};
app.api_messages.push(crate::models::Message {
app.api_messages.push(codewhale_models::Message {
role: Role::User,
content: vec![ContentBlock::Text {
text: format!("{instruction}\n\n---\n\nUser request: {display}"),
@@ -722,7 +722,7 @@ mod tests {
workflow(&mut app, Some("confirm")).is_error,
"a failed or unfinished draft turn is not a reviewed plan"
);
app.api_messages.push(crate::models::Message {
app.api_messages.push(codewhale_models::Message {
role: Role::Assistant,
content: vec![ContentBlock::Text {
text: "Objective, phases, workers, and risks. Run /workflow confirm to start."
@@ -748,7 +748,7 @@ mod tests {
"only the later explicit confirmation restores the normal catalog"
);
app.api_messages.push(crate::models::Message {
app.api_messages.push(codewhale_models::Message {
role: Role::User,
content: vec![ContentBlock::Text {
text: instruction,
@@ -851,14 +851,14 @@ mod tests {
Some(Vec::new())
);
app.api_messages.push(crate::models::Message {
app.api_messages.push(codewhale_models::Message {
role: Role::User,
content: vec![ContentBlock::Text {
text: instruction,
cache_control: None,
}],
});
app.api_messages.push(crate::models::Message {
app.api_messages.push(codewhale_models::Message {
role: Role::Assistant,
content: vec![ContentBlock::Text {
text: "Saved Workflow path and risks reviewed.".to_string(),
@@ -4,9 +4,9 @@ use std::time::Instant;
use super::CommandResult;
use crate::client::{CacheWarmupKey, PromptInspection, inspect_prompt_for_request};
use crate::models::MessageRequest;
use crate::tui::app::{App, AppAction, TurnCacheRecord};
use codewhale_localization::{Locale, MessageId, tr};
use codewhale_models::MessageRequest;
/// Show per-turn DeepSeek prefix-cache telemetry for the last N turns (#263).
///
@@ -675,7 +675,7 @@ fn format_cache_history(app: &App, count: usize, locale: Locale) -> String {
let replay_cell = rec
.reasoning_replay_tokens
.map_or_else(|| "—".to_string(), |t| t.to_string());
let classes = crate::pricing::token_usage_for_pricing(&crate::models::Usage {
let classes = crate::pricing::token_usage_for_pricing(&codewhale_models::Usage {
input_tokens: rec.input_tokens,
output_tokens: rec.output_tokens,
prompt_cache_hit_tokens: rec.cache_hit_tokens,
@@ -187,7 +187,7 @@ fn parse_args(raw: &str) -> Result<PreviewArgs, String> {
mod tests {
use super::*;
use crate::config::Config;
use crate::models::Role;
use codewhale_models::Role;
fn args(raw: &str) -> Result<PreviewArgs, String> {
parse_args(raw)
@@ -366,9 +366,9 @@ mod tests {
"/tmp/test-workspace-preview-request-pure",
));
let mut app = App::new(options, &Config::default());
app.api_messages.push(crate::models::Message {
app.api_messages.push(codewhale_models::Message {
role: Role::User,
content: vec![crate::models::ContentBlock::Text {
content: vec![codewhale_models::ContentBlock::Text {
text: "hello".to_string(),
cache_control: None,
}],
+10 -10
View File
@@ -3,10 +3,10 @@ use super::tokens::{context, cost, system_prompt, tokens};
use super::undo::{patch_undo, prune_undone_tool_context, retry, undo_conversation};
use crate::client::CacheWarmupKey;
use crate::config::Config;
use crate::models::Role;
use crate::models::{ContentBlock, Message, SystemBlock, SystemPrompt, Tool};
use crate::tui::app::{App, AppAction, TuiOptions, TurnCacheRecord};
use crate::tui::history::{GenericToolCell, HistoryCell, ToolCell, ToolStatus};
use codewhale_models::Role;
use codewhale_models::{ContentBlock, Message, SystemBlock, SystemPrompt, Tool};
use std::path::PathBuf;
use std::time::Instant;
@@ -139,7 +139,7 @@ fn cost_report_states_its_coverage_and_names_what_it_excludes() {
use crate::pricing::audit_turn_cost_for_provider_at;
let mut app = create_test_app();
let write_heavy = crate::models::Usage {
let write_heavy = codewhale_models::Usage {
input_tokens: 1_000_000,
output_tokens: 100_000,
prompt_cache_hit_tokens: Some(200_000),
@@ -207,7 +207,7 @@ fn cost_report_states_its_coverage_and_names_what_it_excludes() {
#[test]
fn cost_coverage_is_currency_specific_for_mixed_deepseek_openai() {
let mut app = create_test_app();
let usage = crate::models::Usage {
let usage = codewhale_models::Usage {
input_tokens: 10_000,
output_tokens: 1_000,
..Default::default()
@@ -333,7 +333,7 @@ fn reset_cost_coverage_clears_every_counter() {
use crate::pricing::audit_turn_cost_for_provider_at;
let mut app = create_test_app();
let usage = crate::models::Usage {
let usage = codewhale_models::Usage {
input_tokens: 1_000_000,
output_tokens: 100_000,
prompt_cache_write_tokens: Some(100_000),
@@ -390,7 +390,7 @@ fn tokens_report_says_estimate_and_exposes_coverage_and_cache_write() {
app.record_turn_cost_audit(&crate::pricing::audit_turn_cost_for_provider_at(
crate::config::ApiProvider::Moonshot,
"kimi-k2.7-code",
&crate::models::Usage {
&codewhale_models::Usage {
input_tokens: 1_000_000,
output_tokens: 100_000,
prompt_cache_write_tokens: Some(100_000),
@@ -691,14 +691,14 @@ fn cache_inspect_reports_divergence_from_previous_request() {
));
app.api_messages.push(Message {
role: Role::Assistant,
content: vec![crate::models::ContentBlock::Text {
content: vec![codewhale_models::ContentBlock::Text {
text: "Prior answer".to_string(),
cache_control: None,
}],
});
app.api_messages.push(Message {
role: Role::User,
content: vec![crate::models::ContentBlock::Text {
content: vec![codewhale_models::ContentBlock::Text {
text: "First task".to_string(),
cache_control: None,
}],
@@ -710,7 +710,7 @@ fn cache_inspect_reports_divergence_from_previous_request() {
assert!(first.contains("Static base prefix stability: no previous request"));
if let Some(last) = app.api_messages.last_mut()
&& let Some(crate::models::ContentBlock::Text { text, .. }) = last.content.first_mut()
&& let Some(codewhale_models::ContentBlock::Text { text, .. }) = last.content.first_mut()
{
*text = "Second task".to_string();
}
@@ -919,7 +919,7 @@ fn cache_history_shows_cache_write_tokens_and_explains_unpriced_turns() {
use crate::pricing::audit_turn_cost_for_provider_at;
let mut app = create_test_app();
let write_heavy = crate::models::Usage {
let write_heavy = codewhale_models::Usage {
input_tokens: 1_000_000,
output_tokens: 100_000,
prompt_cache_hit_tokens: Some(200_000),
@@ -1,9 +1,9 @@
//! Token/cost introspection and context commands.
use crate::compaction::estimate_input_tokens_conservative;
use crate::models::SystemPrompt;
use crate::tui::app::{App, AppAction};
use codewhale_localization::{Locale, MessageId, tr};
use codewhale_models::SystemPrompt;
use super::CommandResult;
+1 -1
View File
@@ -1,9 +1,9 @@
//! Undo, retry, edit, and diff commands.
use crate::dependencies::{ExternalTool, Git};
use crate::models::ContentBlock;
use crate::tui::app::{App, AppAction};
use crate::tui::history::HistoryCell;
use codewhale_models::ContentBlock;
use super::CommandResult;
@@ -15,10 +15,10 @@ use regex::Regex;
use serde_json::Value;
use crate::commands::traits::{CommandInfo, RegisterCommand};
use crate::models::{ContentBlock, Message, Role};
use crate::tui::app::App;
use crate::tui::history::HistoryCell;
use codewhale_localization::MessageId;
use codewhale_models::{ContentBlock, Message, Role};
use super::CommandResult;
@@ -950,9 +950,9 @@ fn set_owner_only(_path: &Path) -> std::io::Result<()> {
mod tests {
use super::*;
use crate::config::Config;
use crate::models::{ImageUrlContent, ToolCaller};
use crate::tui::app::{App, TuiOptions};
use crate::tui::clipboard::ClipboardHandler;
use codewhale_models::{ImageUrlContent, ToolCaller};
use tempfile::TempDir;
fn test_app(tmpdir: &TempDir) -> App {
@@ -44,9 +44,9 @@ use serde_json::{Value, json};
use unicode_segmentation::UnicodeSegmentation;
use crate::commands::traits::{CommandInfo, RegisterCommand};
use crate::models::{ContentBlock, Message};
use crate::tui::app::App;
use codewhale_localization::{Locale, MessageId, tr};
use codewhale_models::{ContentBlock, Message};
use super::CommandResult;
use super::export::{is_internal_role, is_sensitive_key, redact_json, sanitize_text};
@@ -1238,11 +1238,11 @@ fn caps_value(caps: &Caps) -> Value {
mod tests {
use super::*;
use crate::config::Config;
use crate::models::Role;
use crate::models::{ImageUrlContent, ToolCaller};
use crate::tools::plan::{PlanItemArg, StepStatus, UpdatePlanArgs};
use crate::tui::app::TuiOptions;
use crate::tui::clipboard::ClipboardHandler;
use codewhale_models::Role;
use codewhale_models::{ImageUrlContent, ToolCaller};
use tempfile::TempDir;
fn test_app(tmpdir: &TempDir) -> App {
@@ -8,12 +8,12 @@ use tempfile::TempDir;
use crate::commands::{self, CommandResult};
use crate::config::Config;
use crate::models::{ContentBlock, Message, Role};
use crate::session_manager::{SavedSession, SessionManager, create_saved_session_with_id_and_mode};
use crate::test_support::{EnvVarGuard, lock_test_env};
use crate::tui::app::{App, AppAction, TuiOptions};
use crate::tui::history::HistoryCell;
use crate::tui::views::ModalKind;
use codewhale_models::{ContentBlock, Message, Role};
const FEATURE_NAME: &str = "Session command workflows";
const FEATURE_PATH: &str = concat!(
@@ -13,12 +13,12 @@ use tempfile::TempDir;
use crate::commands::groups::session::MAX_TITLE_LEN;
use crate::commands::{CommandResult, execute};
use crate::config::{ApiProvider, Config};
use crate::models::{ContentBlock, Message, Role, SystemPrompt};
use crate::session_manager::{SessionManager, create_saved_session_with_mode};
use crate::test_support::{EnvVarGuard, TestEnvLock};
use crate::tui::app::{App, AppAction, TuiOptions};
use codewhale_config::AppMode;
use codewhale_localization::{Locale, MessageId, tr};
use codewhale_models::{ContentBlock, Message, Role, SystemPrompt};
/// Owns the global environment lock for as long as its CODEWHALE_HOME guard.
/// Fields are ordered so the guard restores the environment before the lock
@@ -10,13 +10,13 @@ use tempfile::TempDir;
use crate::commands::CommandResult;
use crate::config::Config;
use crate::models::Role;
use crate::reasoning_preference::ReasoningEffort;
use crate::session_manager::create_saved_session_with_id_and_mode;
use crate::test_support::EnvVarGuard;
use crate::tui::app::{App, AppAction, TuiOptions, TurnCacheRecord};
use crate::tui::history::HistoryCell;
use codewhale_config::AppMode;
use codewhale_models::Role;
fn dispatch_lifecycle(app: &mut App, name: &str, arg: Option<&str>) -> CommandResult {
let command = match arg {
@@ -170,9 +170,9 @@ fn fork_saves_parent_and_switches_to_child_session() {
.expect("fixed parent timestamp");
app.current_session_metadata = Some(cached_parent.clone());
app.session_title = Some(cached_parent.title.clone());
app.api_messages.push(crate::models::Message {
app.api_messages.push(codewhale_models::Message {
role: Role::User,
content: vec![crate::models::ContentBlock::Text {
content: vec![codewhale_models::ContentBlock::Text {
text: "try another path".to_string(),
cache_control: None,
}],
@@ -258,9 +258,9 @@ fn fork_rejects_active_runtime_without_switching_sessions() {
let tmpdir = TempDir::new().unwrap();
let mut app = create_test_app_with_tmpdir(&tmpdir);
app.current_session_id = Some("parent-session".to_string());
app.api_messages.push(crate::models::Message {
app.api_messages.push(codewhale_models::Message {
role: Role::User,
content: vec![crate::models::ContentBlock::Text {
content: vec![codewhale_models::ContentBlock::Text {
text: "still running".to_string(),
cache_control: None,
}],
@@ -281,9 +281,9 @@ fn new_session_from_resumed_state_creates_distinct_empty_session() {
let mut app = create_test_app_with_tmpdir(&tmpdir);
app.current_session_id = Some("old-session".to_string());
app.session_title = Some("Old Session".to_string());
app.api_messages.push(crate::models::Message {
app.api_messages.push(codewhale_models::Message {
role: Role::User,
content: vec![crate::models::ContentBlock::Text {
content: vec![codewhale_models::ContentBlock::Text {
text: "continue this thread".to_string(),
cache_control: None,
}],
@@ -291,7 +291,9 @@ fn new_session_from_resumed_state_creates_distinct_empty_session() {
app.add_message(HistoryCell::System {
content: "old transcript".to_string(),
});
app.system_prompt = Some(crate::models::SystemPrompt::Text("old prompt".to_string()));
app.system_prompt = Some(codewhale_models::SystemPrompt::Text(
"old prompt".to_string(),
));
app.session.total_tokens = 123;
app.session.session_cost = 1.25;
@@ -384,7 +386,7 @@ fn new_session_force_cannot_detach_an_in_flight_turn() {
let tmpdir = TempDir::new().unwrap();
let mut app = create_test_app_with_tmpdir(&tmpdir);
app.current_session_id = Some("old-session".to_string());
app.api_messages.push(crate::models::Message {
app.api_messages.push(codewhale_models::Message {
role: Role::User,
content: vec![],
});
@@ -410,7 +412,7 @@ fn load_rejects_an_active_runtime_before_reading_or_mutating() {
let tmpdir = TempDir::new().unwrap();
let mut app = create_test_app_with_tmpdir(&tmpdir);
app.current_session_id = Some("old-session".to_string());
app.api_messages.push(crate::models::Message {
app.api_messages.push(codewhale_models::Message {
role: Role::User,
content: vec![],
});
@@ -529,9 +531,9 @@ fn test_load_valid_session_defers_state_restore_to_event_loop() {
let tmpdir = TempDir::new().unwrap();
let mut app1 = create_test_app_with_tmpdir(&tmpdir);
// Set up some state to save
app1.api_messages.push(crate::models::Message {
app1.api_messages.push(codewhale_models::Message {
role: Role::User,
content: vec![crate::models::ContentBlock::Text {
content: vec![codewhale_models::ContentBlock::Text {
text: "Hello".to_string(),
cache_control: None,
}],
@@ -543,7 +545,7 @@ fn test_load_valid_session_defers_state_restore_to_event_loop() {
// Create new app and load
let mut app2 = create_test_app_with_tmpdir(&tmpdir);
app2.system_prompt = Some(crate::models::SystemPrompt::Text(
app2.system_prompt = Some(codewhale_models::SystemPrompt::Text(
"stale prompt from prior session".to_string(),
));
app2.session_context_references
@@ -614,7 +616,7 @@ fn explicit_save_persists_work_state_and_load_defers_application() {
fn new_session_is_all_or_nothing_when_work_state_is_busy() {
let tmpdir = TempDir::new().unwrap();
let mut app = create_test_app_with_tmpdir(&tmpdir);
app.api_messages.push(crate::models::Message {
app.api_messages.push(codewhale_models::Message {
role: Role::User,
content: vec![],
});
@@ -706,9 +708,9 @@ fn load_defers_artifact_registry_restore_to_event_loop() {
fn load_defers_telemetry_reset_to_event_loop() {
let tmpdir = TempDir::new().unwrap();
let mut saved_app = create_test_app_with_tmpdir(&tmpdir);
saved_app.api_messages.push(crate::models::Message {
saved_app.api_messages.push(codewhale_models::Message {
role: Role::User,
content: vec![crate::models::ContentBlock::Text {
content: vec![codewhale_models::ContentBlock::Text {
text: "checkpoint".to_string(),
cache_control: None,
}],
+13 -13
View File
@@ -8,8 +8,8 @@ use std::time::Duration;
use crate::config::DEFAULT_TEXT_MODEL;
use crate::core::model_client::ModelClient;
use crate::logging;
use crate::models::Role;
use crate::models::{
use codewhale_models::Role;
use codewhale_models::{
CacheControl, ContentBlock, Message, MessageRequest, SystemBlock, SystemPrompt, Usage,
};
@@ -1536,10 +1536,10 @@ async fn create_summary(
// Usage above is already billed; a provider-declared incomplete
// summary must still fail rather than replace the session history
// with a fragment.
if crate::models::is_incomplete_stop_reason(response.stop_reason.as_deref()) {
if codewhale_models::is_incomplete_stop_reason(response.stop_reason.as_deref()) {
anyhow::bail!(
"Compaction summary response incomplete: provider stop reason `{}`; the partial summary was not accepted.",
crate::models::stop_reason_detail(response.stop_reason.as_deref())
codewhale_models::stop_reason_detail(response.stop_reason.as_deref())
);
}
@@ -1633,7 +1633,7 @@ mod quota_tests;
#[cfg(test)]
mod tests {
use crate::models::{ImageUrlContent, Message};
use codewhale_models::{ImageUrlContent, Message};
#[test]
fn inline_image_estimates_nonzero_tokens() {
@@ -1970,7 +1970,7 @@ mod tests {
async fn create_message(
&self,
request: MessageRequest,
) -> anyhow::Result<crate::models::MessageResponse> {
) -> anyhow::Result<codewhale_models::MessageResponse> {
self.requests
.lock()
.expect("capture scripted summary request")
@@ -1988,7 +1988,7 @@ mod tests {
}
let content = outcome
.ok_or_else(|| anyhow::anyhow!("scripted summary responses exhausted"))??;
Ok(crate::models::MessageResponse {
Ok(codewhale_models::MessageResponse {
id: "summary-scripted".to_string(),
r#type: "message".to_string(),
role: "assistant".to_string(),
@@ -2032,9 +2032,9 @@ mod tests {
async fn create_message(
&self,
request: MessageRequest,
) -> anyhow::Result<crate::models::MessageResponse> {
) -> anyhow::Result<codewhale_models::MessageResponse> {
*self.request.lock().expect("capture summary request") = Some(request);
Ok(crate::models::MessageResponse {
Ok(codewhale_models::MessageResponse {
id: "summary-fixture".to_string(),
r#type: "message".to_string(),
role: "assistant".to_string(),
@@ -2046,7 +2046,7 @@ mod tests {
stop_reason: None,
stop_sequence: None,
container: None,
usage: crate::models::Usage::default(),
usage: codewhale_models::Usage::default(),
})
}
@@ -2572,8 +2572,8 @@ mod tests {
async fn create_message(
&self,
_request: MessageRequest,
) -> anyhow::Result<crate::models::MessageResponse> {
Ok(crate::models::MessageResponse {
) -> anyhow::Result<codewhale_models::MessageResponse> {
Ok(codewhale_models::MessageResponse {
id: "summary-truncated".to_string(),
r#type: "message".to_string(),
role: "assistant".to_string(),
@@ -2585,7 +2585,7 @@ mod tests {
stop_reason: Some("max_tokens".to_string()),
stop_sequence: None,
container: None,
usage: crate::models::Usage::default(),
usage: codewhale_models::Usage::default(),
})
}
+2 -2
View File
@@ -6,7 +6,7 @@
use anyhow::Result;
use crate::models::{ContentBlock, Message, SystemPrompt};
use codewhale_models::{ContentBlock, Message, SystemPrompt};
use super::{
compaction_checkpoint_message, is_compaction_checkpoint_message, retained_user_messages,
@@ -455,7 +455,7 @@ pub(super) fn build_replacement_history(
mod tests {
use super::*;
use crate::compaction::{COMPACTION_SUMMARY_MARKER, compaction_checkpoint_message};
use crate::models::{ContentBlock, Role};
use codewhale_models::{ContentBlock, Role};
use serde_json::json;
fn msg(role: &str, text: &str) -> Message {
+26 -26
View File
@@ -688,10 +688,10 @@ pub fn provider_capability_with_wire(
return ProviderCapability {
provider,
resolved_model: resolved_model.to_string(),
context_window: crate::models::context_window_for_model(resolved_model)
.unwrap_or(crate::models::LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS),
max_output: crate::models::max_output_tokens_for_model(resolved_model),
thinking_supported: crate::models::model_supports_reasoning(resolved_model),
context_window: codewhale_models::context_window_for_model(resolved_model)
.unwrap_or(codewhale_models::LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS),
max_output: codewhale_models::max_output_tokens_for_model(resolved_model),
thinking_supported: codewhale_models::model_supports_reasoning(resolved_model),
cache_telemetry_supported: false,
request_payload_mode: RequestPayloadMode::AnthropicMessages,
alias_deprecation: None,
@@ -701,10 +701,10 @@ pub fn provider_capability_with_wire(
return ProviderCapability {
provider,
resolved_model: resolved_model.to_string(),
context_window: crate::models::context_window_for_model(resolved_model)
.unwrap_or(crate::models::LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS),
max_output: crate::models::max_output_tokens_for_model(resolved_model),
thinking_supported: crate::models::model_supports_reasoning(resolved_model),
context_window: codewhale_models::context_window_for_model(resolved_model)
.unwrap_or(codewhale_models::LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS),
max_output: codewhale_models::max_output_tokens_for_model(resolved_model),
thinking_supported: codewhale_models::model_supports_reasoning(resolved_model),
cache_telemetry_supported: false,
request_payload_mode: RequestPayloadMode::Responses,
alias_deprecation: None,
@@ -721,7 +721,7 @@ pub fn provider_capability_with_wire(
resolved_model: resolved_model.to_string(),
// 200K is the conservative Anthropic floor; 4.6+ models resolve
// their 1M windows from models.rs rows (#3014).
context_window: crate::models::context_window_for_model(resolved_model)
context_window: codewhale_models::context_window_for_model(resolved_model)
.unwrap_or(200_000),
// 64K is the documented Anthropic Messages floor. For a model
// the catalogue describes this carries its documented ceiling;
@@ -729,9 +729,9 @@ pub fn provider_capability_with_wire(
// `route_budget::output_ceiling_source` labels it unverified so
// no receipt renders it as "documented" (#5440).
max_output: Some(
crate::models::max_output_tokens_for_model(resolved_model).unwrap_or(64_000),
codewhale_models::max_output_tokens_for_model(resolved_model).unwrap_or(64_000),
),
thinking_supported: crate::models::model_supports_reasoning(resolved_model),
thinking_supported: codewhale_models::model_supports_reasoning(resolved_model),
cache_telemetry_supported: matches!(provider, ApiProvider::Anthropic),
request_payload_mode: RequestPayloadMode::AnthropicMessages,
alias_deprecation: None,
@@ -766,12 +766,12 @@ pub fn provider_capability_with_wire(
return ProviderCapability {
provider,
resolved_model: resolved_model.to_string(),
context_window: crate::models::context_window_for_model(resolved_model)
.unwrap_or(crate::models::LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS),
context_window: codewhale_models::context_window_for_model(resolved_model)
.unwrap_or(codewhale_models::LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS),
// No documented output maximum for these routes: stay unknown so
// no compatibility clamp is applied downstream.
max_output: crate::models::max_output_tokens_for_model(resolved_model),
thinking_supported: crate::models::model_supports_reasoning(resolved_model),
max_output: codewhale_models::max_output_tokens_for_model(resolved_model),
thinking_supported: codewhale_models::model_supports_reasoning(resolved_model),
cache_telemetry_supported: false,
request_payload_mode: RequestPayloadMode::ChatCompletions,
alias_deprecation: None,
@@ -782,12 +782,12 @@ pub fn provider_capability_with_wire(
return ProviderCapability {
provider,
resolved_model: resolved_model.to_string(),
context_window: crate::models::context_window_for_model(resolved_model)
.unwrap_or(crate::models::LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS),
context_window: codewhale_models::context_window_for_model(resolved_model)
.unwrap_or(codewhale_models::LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS),
// No documented output maximum for these routes: stay unknown so
// no compatibility clamp is applied downstream.
max_output: crate::models::max_output_tokens_for_model(resolved_model),
thinking_supported: crate::models::model_supports_reasoning(resolved_model),
max_output: codewhale_models::max_output_tokens_for_model(resolved_model),
thinking_supported: codewhale_models::model_supports_reasoning(resolved_model),
cache_telemetry_supported: false,
request_payload_mode: RequestPayloadMode::ChatCompletions,
alias_deprecation: None,
@@ -815,8 +815,8 @@ pub fn provider_capability_with_wire(
let offering =
crate::provider_lake::bundled_catalog_offering_for_model(provider, resolved_model);
let context_window = if is_v4_pro || is_v4_flash {
crate::models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
} else if let Some(window) = crate::models::context_window_for_model(resolved_model) {
codewhale_models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
} else if let Some(window) = codewhale_models::context_window_for_model(resolved_model) {
window
} else if let Some(window) = offering
.as_ref()
@@ -828,7 +828,7 @@ pub fn provider_capability_with_wire(
} else if matches!(provider, ApiProvider::Ollama) {
8192
} else {
crate::models::LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS
codewhale_models::LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS
};
// Output limits require an exact catalog row or an explicitly recognized
@@ -839,7 +839,7 @@ pub fn provider_capability_with_wire(
// are owned by the membership catalog. It must not become a placeholder
// number: a fabricated 4K here silently clamped offline membership routes
// to 4K output via `route_budget`.
let max_output = crate::models::max_output_tokens_for_model(resolved_model)
let max_output = codewhale_models::max_output_tokens_for_model(resolved_model)
.or_else(|| {
// Provider-owned wire IDs need not exist in the legacy model-only
// catalog (for example Fireworks' accounts/... slug). Reuse the
@@ -857,7 +857,7 @@ pub fn provider_capability_with_wire(
.as_ref()
.map(|_| DEEPSEEK_ALIAS_REPLACEMENT)
})
.and_then(crate::models::max_output_tokens_for_model)
.and_then(codewhale_models::max_output_tokens_for_model)
});
// Exact catalog reasoning facts and recognized compatibility aliases only.
@@ -867,7 +867,7 @@ pub fn provider_capability_with_wire(
|| offering
.as_ref()
.is_some_and(|row| row.reasoning == Some(true))
|| crate::models::model_supports_reasoning(resolved_model);
|| codewhale_models::model_supports_reasoning(resolved_model);
// Cache telemetry: returned only by DeepSeek-native and NVIDIA NIM endpoints.
let cache_telemetry_supported = matches!(
@@ -1067,7 +1067,7 @@ pub fn validate_route(provider: ApiProvider, model: &str) -> Result<(), String>
Ok(())
}
use crate::models::canonical_official_deepseek_model_id;
use codewhale_models::canonical_official_deepseek_model_id;
/// Resolve model names accepted by DeepSeek's first-party endpoints.
///
+2 -2
View File
@@ -126,11 +126,11 @@ pub const KIMI_CODE_K3_256K_MODEL: &str = "k3-256k";
pub const KIMI_CODE_HIGHSPEED_MODEL: &str = "kimi-for-coding-highspeed";
// The K3 contract constants (`KIMI_CODE_K3_CONTEXT_WINDOW_TOKENS`,
// `KIMI_K3_CONTEXT_WINDOW_TOKENS`, and the distinct default/direct output
// limits) live in `crate::models` — the model-facts table, which also compiles
// limits) live in `codewhale_models` — the model-facts table, which also compiles
// standalone in integration tests — so the facts have exactly one home.
// Re-export only the route-owned floor, which existing `crate::config` call
// sites import.
pub use crate::models::KIMI_CODE_K3_CONTEXT_WINDOW_TOKENS;
pub use codewhale_models::KIMI_CODE_K3_CONTEXT_WINDOW_TOKENS;
pub const DEFAULT_SGLANG_MODEL: &str = "deepseek-ai/DeepSeek-V4-Pro";
pub const DEFAULT_SGLANG_FLASH_MODEL: &str = "deepseek-ai/DeepSeek-V4-Flash";
pub const DEFAULT_SGLANG_BASE_URL: &str = "http://localhost:30000/v1";
+22 -22
View File
@@ -11426,7 +11426,7 @@ fn provider_capability_scenario() {
let cap = provider_capability(ApiProvider::Deepseek, "deepseek-v4-pro");
assert_eq!(
cap.context_window,
crate::models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
codewhale_models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
);
assert_eq!(cap.max_output, Some(384_000));
assert!(cap.thinking_supported);
@@ -11444,7 +11444,7 @@ fn provider_capability_scenario() {
);
assert_eq!(
cap.context_window,
crate::models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
codewhale_models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
);
assert_eq!(cap.max_output, Some(384_000));
assert!(cap.thinking_supported);
@@ -11461,12 +11461,12 @@ fn provider_capability_scenario() {
assert_eq!(cap.resolved_model, DEFAULT_OPENMODEL_MODEL);
assert_eq!(
cap.context_window,
crate::models::context_window_for_model(DEFAULT_OPENMODEL_MODEL).unwrap_or(200_000)
codewhale_models::context_window_for_model(DEFAULT_OPENMODEL_MODEL).unwrap_or(200_000)
);
assert_eq!(
cap.max_output,
Some(
crate::models::max_output_tokens_for_model(DEFAULT_OPENMODEL_MODEL)
codewhale_models::max_output_tokens_for_model(DEFAULT_OPENMODEL_MODEL)
.unwrap_or(64_000)
)
);
@@ -11482,7 +11482,7 @@ fn provider_capability_scenario() {
let cap = provider_capability(ApiProvider::Deepseek, "deepseek-v4-flash");
assert_eq!(
cap.context_window,
crate::models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
codewhale_models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
);
assert_eq!(cap.max_output, Some(384_000));
assert!(cap.thinking_supported);
@@ -11493,7 +11493,7 @@ fn provider_capability_scenario() {
let cap = provider_capability(ApiProvider::Deepseek, "deepseek-chat");
assert_eq!(
cap.context_window,
crate::models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
codewhale_models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
);
assert_eq!(cap.max_output, Some(384_000));
assert!(cap.thinking_supported);
@@ -11513,7 +11513,7 @@ fn provider_capability_scenario() {
let cap = provider_capability(ApiProvider::Deepseek, "deepseek-reasoner");
assert_eq!(
cap.context_window,
crate::models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
codewhale_models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
);
assert_eq!(cap.max_output, Some(384_000));
assert!(cap.thinking_supported);
@@ -11536,7 +11536,7 @@ fn provider_capability_scenario() {
let cap = provider_capability(ApiProvider::NvidiaNim, DEFAULT_NVIDIA_NIM_MODEL);
assert_eq!(
cap.context_window,
crate::models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
codewhale_models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
);
assert_eq!(cap.max_output, Some(384_000));
assert!(cap.thinking_supported);
@@ -11556,7 +11556,7 @@ fn provider_capability_scenario_2() {
let cap = provider_capability(ApiProvider::NvidiaNim, DEFAULT_NVIDIA_NIM_FLASH_MODEL);
assert_eq!(
cap.context_window,
crate::models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
codewhale_models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
);
assert_eq!(cap.max_output, Some(384_000));
assert!(cap.thinking_supported);
@@ -11581,11 +11581,11 @@ fn provider_capability_scenario_2() {
let cap = provider_capability(ApiProvider::Together, TOGETHER_INKLING_MODEL);
assert!(cap.thinking_supported);
assert_eq!(
crate::models::context_window_for_model(TOGETHER_INKLING_MODEL),
codewhale_models::context_window_for_model(TOGETHER_INKLING_MODEL),
None
);
assert_eq!(
crate::models::max_output_tokens_for_model(TOGETHER_INKLING_MODEL),
codewhale_models::max_output_tokens_for_model(TOGETHER_INKLING_MODEL),
None
);
}
@@ -11612,7 +11612,7 @@ fn provider_capability_scenario_2() {
let cap = provider_capability(ApiProvider::Novita, DEFAULT_NOVITA_MODEL);
assert_eq!(
cap.context_window,
crate::models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
codewhale_models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
);
assert_eq!(cap.max_output, Some(384_000));
assert!(cap.thinking_supported);
@@ -11623,7 +11623,7 @@ fn provider_capability_scenario_2() {
let cap = provider_capability(ApiProvider::Fireworks, DEFAULT_FIREWORKS_MODEL);
assert_eq!(
cap.context_window,
crate::models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
codewhale_models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
);
assert_eq!(cap.max_output, Some(384_000));
assert!(cap.thinking_supported);
@@ -11654,7 +11654,7 @@ fn provider_capability_scenario_2() {
let cap = provider_capability(ApiProvider::Siliconflow, DEFAULT_SILICONFLOW_MODEL);
assert_eq!(
cap.context_window,
crate::models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
codewhale_models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
);
assert_eq!(cap.max_output, Some(384_000));
assert!(cap.thinking_supported);
@@ -11669,7 +11669,7 @@ fn provider_capability_scenario_2() {
let cap = provider_capability(ApiProvider::Sglang, DEFAULT_SGLANG_MODEL);
assert_eq!(
cap.context_window,
crate::models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
codewhale_models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
);
assert_eq!(cap.max_output, Some(384_000));
assert!(cap.thinking_supported);
@@ -11682,7 +11682,7 @@ fn provider_capability_openrouter_v4_pro_has_thinking_no_cache() {
let cap = provider_capability(ApiProvider::Openrouter, DEFAULT_OPENROUTER_MODEL);
assert_eq!(
cap.context_window,
crate::models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
codewhale_models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
);
assert_eq!(cap.max_output, Some(384_000));
assert!(cap.thinking_supported);
@@ -11801,7 +11801,7 @@ fn provider_capability_arcee_direct_models_use_api_docs_shape() {
// now says so instead of fabricating a 4K request fallback.
assert_eq!(mini.max_output, None);
assert_eq!(
crate::models::max_output_tokens_for_model(ARCEE_TRINITY_MINI_MODEL),
codewhale_models::max_output_tokens_for_model(ARCEE_TRINITY_MINI_MODEL),
None
);
assert!(mini.thinking_supported);
@@ -11820,7 +11820,7 @@ fn provider_capability_scenario_3() {
let cap = provider_capability(ApiProvider::Openai, "glm-5");
assert_eq!(
cap.context_window,
crate::models::LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS
codewhale_models::LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS
);
assert_eq!(cap.max_output, None);
assert!(!cap.thinking_supported);
@@ -11838,7 +11838,7 @@ fn provider_capability_scenario_3() {
let cap = provider_capability(ApiProvider::Atlascloud, "deepseek-ai/deepseek-v4-flash");
assert_eq!(
cap.context_window,
crate::models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
codewhale_models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS
);
assert_eq!(cap.max_output, Some(384_000));
assert!(cap.thinking_supported);
@@ -11877,7 +11877,7 @@ fn provider_capability_scenario_3() {
let cap = provider_capability(ApiProvider::WanjieArk, DEFAULT_WANJIE_ARK_MODEL);
assert_eq!(
cap.context_window,
crate::models::LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS
codewhale_models::LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS
);
assert_eq!(cap.max_output, None);
assert!(cap.thinking_supported);
@@ -11909,7 +11909,7 @@ fn provider_capability_scenario_3() {
let cap = provider_capability(ApiProvider::Ollama, "deepseek-v3.1:671b");
assert_eq!(cap.context_window, 8192);
assert_eq!(
crate::models::context_window_for_model("deepseek-v3.1:671b"),
codewhale_models::context_window_for_model("deepseek-v3.1:671b"),
None
);
assert_eq!(cap.max_output, None);
@@ -12039,7 +12039,7 @@ fn provider_capability_scenario_4() {
let cap = provider_capability(ApiProvider::Deepseek, "deepseek-coder");
assert_eq!(
cap.context_window,
crate::models::LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS
codewhale_models::LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS
);
assert_eq!(cap.max_output, None);
assert!(!cap.thinking_supported);
+4 -4
View File
@@ -13,11 +13,11 @@ use serde::Serialize;
use crate::compaction::{estimate_input_tokens_conservative, estimate_text_tokens_conservative};
use crate::config::Config;
use crate::context_budget::PressureLevel;
use crate::models::{CacheControl, ContentBlock, Message, SystemPrompt, Tool};
use crate::prompts::{CORE_EXECUTION_PROFILE_PROMPT, Personality};
use crate::route_budget::route_context_window_tokens;
use crate::tui::app::App;
use codewhale_config::AppMode;
use codewhale_models::{CacheControl, ContentBlock, Message, SystemPrompt, Tool};
#[derive(Debug, Clone, Serialize)]
pub struct PromptSourceMap {
@@ -955,10 +955,10 @@ pub fn prompt_context_json(context: &PromptContext) -> String {
mod tests {
use super::*;
use crate::config::{ApiProvider, Config};
use crate::models::Role;
use crate::models::Tool;
use crate::route_runtime::{ContextWindowResolution, ContextWindowSource};
use codewhale_config::route::RouteLimits;
use codewhale_models::Role;
use codewhale_models::Tool;
use std::fs;
use tempfile::tempdir;
@@ -1377,7 +1377,7 @@ mod tests {
// deepseek-v4-pro defaults to a 1M window; a resolved route advertising a
// smaller window must win in the report's context_window_tokens.
let route_window = 128_000u64;
let model_default = crate::models::context_window_for_model("deepseek-v4-pro")
let model_default = codewhale_models::context_window_for_model("deepseek-v4-pro")
.expect("model has a default window");
assert_ne!(
u64::from(model_default),
+9 -9
View File
@@ -32,12 +32,6 @@ use crate::core::model_client::SharedModelClient;
use crate::error_taxonomy::{ErrorCategory, ErrorEnvelope, ErrorSeverity, StreamError};
use crate::features::{Feature, Features};
use crate::mcp::{McpConfig, McpPool};
#[cfg(test)]
use crate::models::ToolCaller;
use crate::models::{
ContentBlock, ContentBlockStart, Delta, Message, StreamEvent, SystemPrompt, Tool, Usage,
is_incomplete_stop_reason, is_output_limit_stop_reason, stop_reason_detail,
};
use crate::prompts;
use crate::purge::{emit_purge_completed, emit_purge_failed, emit_purge_started, run_purge};
#[cfg(test)]
@@ -71,6 +65,12 @@ use crate::worker_profile::WorkerRuntimeProfile;
use crate::working_set::WorkingSet;
use codewhale_config::AppMode;
use codewhale_execpolicy::ApprovalMode;
#[cfg(test)]
use codewhale_models::ToolCaller;
use codewhale_models::{
ContentBlock, ContentBlockStart, Delta, Message, StreamEvent, SystemPrompt, Tool, Usage,
is_incomplete_stop_reason, is_output_limit_stop_reason, stop_reason_detail,
};
#[cfg(test)]
use super::authority::agent_approval_mode_for_turn;
@@ -85,7 +85,7 @@ use super::ops::{
use super::session::Session;
use super::tool_parser;
use super::turn::{TurnContext, post_turn_snapshot, pre_turn_snapshot};
use crate::models::Role;
use codewhale_models::Role;
const ENGINE_OP_CHANNEL_CAPACITY: usize = 32;
const GOAL_CONTINUATION_FAILURE_DETAIL_MAX_BYTES: usize = 512;
@@ -3539,7 +3539,7 @@ impl Engine {
let already_committed = self.session.messages.last().is_some_and(|last| {
matches!(
last.role.as_str(),
"assistant" | crate::models::INTERRUPTED_ASSISTANT_ROLE
"assistant" | codewhale_models::INTERRUPTED_ASSISTANT_ROLE
) && last.content == message.content
});
if already_committed {
@@ -5518,7 +5518,7 @@ impl Engine {
})
.clone();
let advisor_messages: Vec<crate::models::Message> = self.session.messages.to_vec();
let advisor_messages: Vec<codewhale_models::Message> = self.session.messages.to_vec();
let advisor_config = self.config.advisor_config.clone();
// This clone is frozen before the detached task starts and keeps
// every configured provider route available for an explicit
+1 -1
View File
@@ -288,13 +288,13 @@ mod tests {
use crate::core::engine::tool_catalog::ToolSurfacePolicy;
use crate::core::turn::TurnContext;
use crate::llm_client::mock::{MockLlmClient, canned};
use crate::models::{ContentBlock, Message, Role, StreamEvent, Usage};
use crate::sandbox::SandboxPolicy;
use crate::tools::spec::{
ApprovalRequirement, ToolCapability, ToolContext, ToolResult, ToolSpec,
};
use codewhale_config::AppMode;
use codewhale_execpolicy::ApprovalMode;
use codewhale_models::{ContentBlock, Message, Role, StreamEvent, Usage};
use serde_json::{Value, json};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
+1 -1
View File
@@ -6,12 +6,12 @@
use crate::config::ApiProvider;
use crate::context_budget::ContextBudget;
use crate::models::SystemPrompt;
#[cfg(test)]
pub(super) use crate::route_budget::effective_max_output_tokens;
pub(super) use crate::route_budget::effective_max_output_tokens_for_route;
use crate::tools::spec::ToolResult;
use codewhale_config::route::RouteLimits;
use codewhale_models::SystemPrompt;
use serde_json::Value;
/// Keep this many most recent messages when emergency trimming is required.
pub(super) const MIN_RECENT_MESSAGES_TO_KEEP: usize = 4;
+1 -1
View File
@@ -16,11 +16,11 @@
use serde_json::json;
use crate::models::{Tool, ToolCaller};
use crate::tools::spec::{
ResourceClaim, ToolError, ToolExecutionOutcome, ToolResult, ToolResultContentBlock,
schedule_non_conflicting,
};
use codewhale_models::{Tool, ToolCaller};
use super::ToolUseState;
+5 -5
View File
@@ -13,16 +13,16 @@
use std::time::Duration;
use crate::core::model_client::ModelClient;
use crate::models::Role;
use crate::models::{
ContentBlock, Message, MessageRequest, MessageResponse, SystemPrompt, Usage,
is_incomplete_stop_reason,
};
use crate::tools::spec::ToolError;
use crate::tui::auto_review::{
AutoReviewAction, DEFAULT_GUARDIAN_POLICY, ReviewerRiskLevel, ReviewerVerdict,
parse_reviewer_verdict,
};
use codewhale_models::Role;
use codewhale_models::{
ContentBlock, Message, MessageRequest, MessageResponse, SystemPrompt, Usage,
is_incomplete_stop_reason,
};
use tokio_util::sync::CancellationToken;
/// One-shot reviewer deadline. Slow reviewers are denials, surfaced
+1 -1
View File
@@ -4,7 +4,7 @@
//! content block kind tracking, streamed tool-use buffers, transparent retry
//! policy, and scrubbers for text that looks like a forged tool-call wrapper.
use crate::models::ToolCaller;
use codewhale_models::ToolCaller;
use std::time::Duration;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+71 -71
View File
@@ -9,12 +9,12 @@ use super::turn_loop::{
requested_sandbox_escalation, sandbox_escalation_denial, workspace_write_carve_out_applies,
};
use crate::config::ApiProvider;
use crate::models::{SystemBlock, Usage};
use crate::prompts::{
InstructionSource, PromptSessionContext, system_prompt_flat_text,
system_prompt_for_mode_with_context_skills_and_session,
};
use crate::test_support::{EnvVarGuard, lock_test_env};
use codewhale_models::{SystemBlock, Usage};
use serde_json::{Value, json};
use std::collections::{HashMap, HashSet};
use std::ffi::OsString;
@@ -497,8 +497,8 @@ impl crate::core::model_client::ModelClient for BlockingEmergencyCompactionModel
async fn create_message(
&self,
_request: crate::models::MessageRequest,
) -> anyhow::Result<crate::models::MessageResponse> {
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<codewhale_models::MessageResponse> {
let _drop_signal = DropSignal(std::sync::Arc::clone(&self.request_dropped));
self.entered.notify_one();
std::future::pending().await
@@ -506,7 +506,7 @@ impl crate::core::model_client::ModelClient for BlockingEmergencyCompactionModel
async fn create_message_stream(
&self,
_request: crate::models::MessageRequest,
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<crate::llm_client::StreamEventBox> {
anyhow::bail!("emergency compaction uses the non-streaming model boundary")
}
@@ -1046,7 +1046,7 @@ async fn exact_turn_snapshot_restores_custom_endpoint_and_turn_receipt_after_bui
struct GatedGoalModelClient {
calls: std::sync::atomic::AtomicUsize,
requests: std::sync::Mutex<Vec<crate::models::MessageRequest>>,
requests: std::sync::Mutex<Vec<codewhale_models::MessageRequest>>,
second_request_entered: std::sync::Arc<tokio::sync::Notify>,
release_second_request: std::sync::Arc<tokio::sync::Notify>,
first_usage: Option<Usage>,
@@ -1082,14 +1082,14 @@ impl crate::core::model_client::ModelClient for IndexedGatedGoalModelClient {
async fn create_message(
&self,
_request: crate::models::MessageRequest,
) -> anyhow::Result<crate::models::MessageResponse> {
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<codewhale_models::MessageResponse> {
anyhow::bail!("indexed gate regression uses the streaming model boundary")
}
async fn create_message_stream(
&self,
_request: crate::models::MessageRequest,
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<crate::llm_client::StreamEventBox> {
let call = self
.calls
@@ -1126,14 +1126,14 @@ impl crate::core::model_client::ModelClient for FirstRequestGatedGoalModelClient
async fn create_message(
&self,
_request: crate::models::MessageRequest,
) -> anyhow::Result<crate::models::MessageResponse> {
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<codewhale_models::MessageResponse> {
anyhow::bail!("mailbox regression uses the streaming model boundary")
}
async fn create_message_stream(
&self,
_request: crate::models::MessageRequest,
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<crate::llm_client::StreamEventBox> {
let call = self
.calls
@@ -1173,14 +1173,14 @@ impl crate::core::model_client::ModelClient for FailingGoalModelClient {
async fn create_message(
&self,
_request: crate::models::MessageRequest,
) -> anyhow::Result<crate::models::MessageResponse> {
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<codewhale_models::MessageResponse> {
anyhow::bail!("failure regression uses the streaming model boundary")
}
async fn create_message_stream(
&self,
_request: crate::models::MessageRequest,
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<crate::llm_client::StreamEventBox> {
self.calls.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
anyhow::bail!(self.message.clone())
@@ -1192,7 +1192,7 @@ impl crate::core::model_client::ModelClient for FailingGoalModelClient {
}
impl GatedGoalModelClient {
fn captured_requests(&self) -> Vec<crate::models::MessageRequest> {
fn captured_requests(&self) -> Vec<codewhale_models::MessageRequest> {
self.requests
.lock()
.expect("goal model request lock")
@@ -1212,14 +1212,14 @@ impl crate::core::model_client::ModelClient for GatedGoalModelClient {
async fn create_message(
&self,
_request: crate::models::MessageRequest,
) -> anyhow::Result<crate::models::MessageResponse> {
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<codewhale_models::MessageResponse> {
anyhow::bail!("goal regression uses the streaming model boundary")
}
async fn create_message_stream(
&self,
request: crate::models::MessageRequest,
request: codewhale_models::MessageRequest,
) -> anyhow::Result<crate::llm_client::StreamEventBox> {
let call = self
.calls
@@ -1239,9 +1239,9 @@ impl crate::core::model_client::ModelClient for GatedGoalModelClient {
let mut events = crate::llm_client::mock::canned::simple_text_turn("still working");
if call == 1
&& let Some(usage) = self.first_usage.clone()
&& let Some(crate::models::StreamEvent::MessageDelta { usage: slot, .. }) = events
&& let Some(codewhale_models::StreamEvent::MessageDelta { usage: slot, .. }) = events
.iter_mut()
.find(|event| matches!(event, crate::models::StreamEvent::MessageDelta { .. }))
.find(|event| matches!(event, codewhale_models::StreamEvent::MessageDelta { .. }))
{
*slot = Some(usage);
}
@@ -4654,8 +4654,8 @@ async fn idle_subagent_delivery_releases_claim_when_route_fails_before_recording
#[test]
fn subagent_mailbox_keeps_lifecycle_events_reliable() {
use crate::models::Usage;
use crate::tools::subagent::MailboxMessage;
use codewhale_models::Usage;
assert!(subagent_mailbox_message_is_best_effort(
&MailboxMessage::progress("agent_a", "step 1")
@@ -4765,8 +4765,8 @@ fn subagent_mailbox_samples_best_effort_events_per_agent() {
#[test]
fn subagent_mailbox_never_samples_lifecycle_or_usage_events() {
use crate::models::Usage;
use crate::tools::subagent::{FleetRole, MailboxMessage};
use codewhale_models::Usage;
let mut last_sent_at = HashMap::new();
let start = Instant::now();
@@ -5506,7 +5506,7 @@ async fn steer_during_final_coordination_response_gets_its_own_provider_reply()
/// Compose one assistant turn that proposes `calls` as a single parallel
/// tool-call batch: `(call_id, tool_name, args_json)` per block, in order.
fn tool_batch_turn(calls: &[(&str, &str, &str)]) -> Vec<crate::models::StreamEvent> {
fn tool_batch_turn(calls: &[(&str, &str, &str)]) -> Vec<codewhale_models::StreamEvent> {
use crate::llm_client::mock::canned;
let mut events = vec![canned::message_start("mock_tool_batch")];
@@ -6265,14 +6265,14 @@ impl crate::core::model_client::ModelClient for FailingGuardianModelClient {
async fn create_message(
&self,
_request: crate::models::MessageRequest,
) -> anyhow::Result<crate::models::MessageResponse> {
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<codewhale_models::MessageResponse> {
anyhow::bail!("fixture guardian transport failure")
}
async fn create_message_stream(
&self,
request: crate::models::MessageRequest,
request: codewhale_models::MessageRequest,
) -> anyhow::Result<crate::llm_client::StreamEventBox> {
crate::core::model_client::ModelClient::create_message_stream(&self.inner, request).await
}
@@ -6294,8 +6294,8 @@ impl crate::core::model_client::ModelClient for BlockingGuardianModelClient {
async fn create_message(
&self,
_request: crate::models::MessageRequest,
) -> anyhow::Result<crate::models::MessageResponse> {
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<codewhale_models::MessageResponse> {
let _drop_signal = DropSignal(std::sync::Arc::clone(&self.guardian_dropped));
self.guardian_entered.notify_one();
std::future::pending().await
@@ -6303,7 +6303,7 @@ impl crate::core::model_client::ModelClient for BlockingGuardianModelClient {
async fn create_message_stream(
&self,
_request: crate::models::MessageRequest,
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<crate::llm_client::StreamEventBox> {
use crate::llm_client::mock::canned;
@@ -6340,14 +6340,14 @@ impl crate::core::model_client::ModelClient for BlockingModelClient {
async fn create_message(
&self,
_request: crate::models::MessageRequest,
) -> anyhow::Result<crate::models::MessageResponse> {
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<codewhale_models::MessageResponse> {
std::future::pending().await
}
async fn create_message_stream(
&self,
_request: crate::models::MessageRequest,
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<crate::llm_client::StreamEventBox> {
let _drop_signal = DropSignal(std::sync::Arc::clone(&self.request_dropped));
self.entered.notify_one();
@@ -6362,7 +6362,7 @@ impl crate::core::model_client::ModelClient for BlockingModelClient {
fn test_tool_surface(
engine: &Engine,
registry: crate::tools::ToolRegistry,
tools: Option<Vec<crate::models::Tool>>,
tools: Option<Vec<codewhale_models::Tool>>,
mode: AppMode,
) -> ToolSurfacePolicy {
ToolSurfacePolicy::new(
@@ -6429,7 +6429,7 @@ async fn tool_request_snapshot_matches_the_exact_mock_request_payload() {
#[tokio::test]
async fn normal_repl_kernel_persists_across_user_turns() {
use crate::llm_client::mock::{MockLlmClient, canned};
use crate::models::{ContentBlock, Message, MessageResponse, Usage};
use codewhale_models::{ContentBlock, Message, MessageResponse, Usage};
let workspace = tempdir().expect("tempdir");
let mock = std::sync::Arc::new(MockLlmClient::new(vec![
@@ -6555,7 +6555,7 @@ async fn normal_repl_kernel_persists_across_user_turns() {
async fn snapshot_for_catalog(
workspace: &Path,
catalog: Option<Vec<crate::models::Tool>>,
catalog: Option<Vec<codewhale_models::Tool>>,
) -> crate::tool_inspection::ToolInspectionSnapshot {
use crate::llm_client::mock::{MockLlmClient, canned};
@@ -6585,7 +6585,7 @@ async fn snapshot_for_catalog(
async fn request_selector_distinguishes_absent_tools_from_present_empty_tools() {
let workspace = tempdir().expect("tempdir");
let absent = snapshot_for_catalog(workspace.path(), None).await;
let deferred_only = crate::models::Tool {
let deferred_only = codewhale_models::Tool {
tool_type: Some("function".to_string()),
name: "deferred_fixture".to_string(),
description: "Deferred fixture".to_string(),
@@ -6874,7 +6874,7 @@ async fn request_snapshot_reports_registry_provenance_for_the_transmitted_catalo
// the turn an empty catalog and nothing would be transmitted. Hand the
// engine an explicit catalog instead: the registry is still the source of
// the *facts*, including the fact that this tool is not model-visible.
let tools = Some(vec![crate::models::Tool {
let tools = Some(vec![codewhale_models::Tool {
tool_type: None,
name: "read_file".to_string(),
description: "Read a file".to_string(),
@@ -8425,8 +8425,8 @@ async fn engine_cancellation_drops_active_injected_model_request() {
task.await.expect("engine task");
}
fn guardian_fixture_response(text: &str) -> crate::models::MessageResponse {
crate::models::MessageResponse {
fn guardian_fixture_response(text: &str) -> codewhale_models::MessageResponse {
codewhale_models::MessageResponse {
id: "guardian-fixture".to_string(),
r#type: "message".to_string(),
role: "assistant".to_string(),
@@ -8447,7 +8447,7 @@ fn guardian_fixture_response(text: &str) -> crate::models::MessageResponse {
}
fn guardian_tool_results<'a>(
request: &'a crate::models::MessageRequest,
request: &'a codewhale_models::MessageRequest,
call_id: &str,
) -> Vec<(&'a str, Option<bool>)> {
request
@@ -8699,7 +8699,7 @@ async fn auto_review_guardian_parse_and_transport_failures_deny_closed() {
r#"{"action":"write","path":".env","content":"must-not-run\n"}"#,
);
let follow_up_id = call_id.clone();
let follow_up = move |request: &crate::models::MessageRequest| {
let follow_up = move |request: &codewhale_models::MessageRequest| {
let results = guardian_tool_results(request, &follow_up_id);
assert_eq!(results.len(), 1, "reviewer failure must pair one result");
let result = results[0];
@@ -11251,7 +11251,7 @@ fn print_mode_runtime_contract_metrics() {
);
let prompt_bytes = system_prompt_flat_text(&prompt).len();
let prompt_blocks = match &prompt {
crate::models::SystemPrompt::Blocks(blocks) => blocks.len(),
codewhale_models::SystemPrompt::Blocks(blocks) => blocks.len(),
_ => 1,
};
mode_metrics.insert(
@@ -11280,7 +11280,7 @@ fn representative_prompt(
instructions: Option<&[InstructionSource]>,
user_memory_block: Option<&str>,
goal_objective: Option<&str>,
) -> crate::models::SystemPrompt {
) -> codewhale_models::SystemPrompt {
system_prompt_for_mode_with_context_skills_and_session(
workspace,
None,
@@ -11296,10 +11296,10 @@ fn representative_prompt(
)
}
fn prompt_block_count(prompt: &crate::models::SystemPrompt) -> usize {
fn prompt_block_count(prompt: &codewhale_models::SystemPrompt) -> usize {
match prompt {
crate::models::SystemPrompt::Blocks(blocks) => blocks.len(),
crate::models::SystemPrompt::Text(_) => 1,
codewhale_models::SystemPrompt::Blocks(blocks) => blocks.len(),
codewhale_models::SystemPrompt::Text(_) => 1,
}
}
@@ -11343,7 +11343,7 @@ fn normalize_representative_prompt(text: &str, workspace: &Path, home: &Path) ->
fn representative_stage(
name: &'static str,
prompt: crate::models::SystemPrompt,
prompt: codewhale_models::SystemPrompt,
workspace: &Path,
home: &Path,
) -> RepresentativePromptStage {
@@ -16600,7 +16600,7 @@ async fn edit_last_turn_without_user_prompt_errors_and_sends_nothing() {
Message {
role: Role::User,
content: vec![ContentBlock::ImageUrl {
image_url: crate::models::ImageUrlContent {
image_url: codewhale_models::ImageUrlContent {
url: "data:image/png;base64,AAAA".to_string(),
},
}],
@@ -20228,14 +20228,14 @@ impl crate::core::model_client::ModelClient for FlakyNetworkDropModelClient {
async fn create_message(
&self,
_request: crate::models::MessageRequest,
) -> anyhow::Result<crate::models::MessageResponse> {
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<codewhale_models::MessageResponse> {
anyhow::bail!("flaky-network regression uses the streaming model boundary")
}
async fn create_message_stream(
&self,
_request: crate::models::MessageRequest,
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<crate::llm_client::StreamEventBox> {
use crate::llm_client::mock::canned;
let call = self
@@ -20256,7 +20256,7 @@ impl crate::core::model_client::ModelClient for FlakyNetworkDropModelClient {
if let StreamEvent::MessageStart { message } = &mut message_start {
message.usage = start_usage;
}
let events: Vec<anyhow::Result<crate::models::StreamEvent>> = vec![
let events: Vec<anyhow::Result<codewhale_models::StreamEvent>> = vec![
Ok(message_start),
Ok(canned::text_block_start(0)),
Ok(canned::text_delta(0, "billed truncated fragment")),
@@ -20279,7 +20279,7 @@ impl crate::core::model_client::ModelClient for FlakyNetworkDropModelClient {
// Partial content first — this flips `any_content_received` so
// the #103 transparent retry cannot fire — then the transport
// dies the way the 0.9.4 Terminal-Bench crashes did.
let events: Vec<anyhow::Result<crate::models::StreamEvent>> = vec![
let events: Vec<anyhow::Result<codewhale_models::StreamEvent>> = vec![
Ok(canned::message_start("flaky_msg")),
Ok(canned::text_block_start(0)),
Ok(canned::text_delta(
@@ -20720,14 +20720,14 @@ impl crate::core::model_client::ModelClient for CancelAfterTerminalUsageModelCli
async fn create_message(
&self,
_request: crate::models::MessageRequest,
) -> anyhow::Result<crate::models::MessageResponse> {
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<codewhale_models::MessageResponse> {
anyhow::bail!("unused")
}
async fn create_message_stream(
&self,
_request: crate::models::MessageRequest,
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<crate::llm_client::StreamEventBox> {
use crate::llm_client::mock::canned;
@@ -21028,14 +21028,14 @@ impl crate::core::model_client::ModelClient for ThinkingOnlyDropModelClient {
async fn create_message(
&self,
_request: crate::models::MessageRequest,
) -> anyhow::Result<crate::models::MessageResponse> {
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<codewhale_models::MessageResponse> {
anyhow::bail!("thinking-only drop regression uses the streaming model boundary")
}
async fn create_message_stream(
&self,
_request: crate::models::MessageRequest,
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<crate::llm_client::StreamEventBox> {
use crate::llm_client::mock::canned;
let call = self
@@ -21047,11 +21047,11 @@ impl crate::core::model_client::ModelClient for ThinkingOnlyDropModelClient {
// visible streams before the transport dies. This still flips
// `any_content_received`, which is what routes the drop to the
// interactive resume path instead of the transparent retry.
let events: Vec<anyhow::Result<crate::models::StreamEvent>> = vec![
let events: Vec<anyhow::Result<codewhale_models::StreamEvent>> = vec![
Ok(canned::message_start("thinking_only_msg")),
Ok(StreamEvent::ContentBlockStart {
index: 0,
content_block: crate::models::ContentBlockStart::Thinking {
content_block: codewhale_models::ContentBlockStart::Thinking {
thinking: String::new(),
},
}),
@@ -21245,7 +21245,7 @@ struct ReasoningOnlyCleanFinishModelClient {
stop_reason: &'static str,
/// Every outbound request's messages, in order, so a test can tell a
/// request-scoped nudge from one written into the session.
requests: std::sync::Mutex<Vec<Vec<crate::models::Message>>>,
requests: std::sync::Mutex<Vec<Vec<codewhale_models::Message>>>,
}
#[async_trait::async_trait]
@@ -21260,14 +21260,14 @@ impl crate::core::model_client::ModelClient for ReasoningOnlyCleanFinishModelCli
async fn create_message(
&self,
_request: crate::models::MessageRequest,
) -> anyhow::Result<crate::models::MessageResponse> {
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<codewhale_models::MessageResponse> {
anyhow::bail!("reasoning-only recovery uses the streaming model boundary")
}
async fn create_message_stream(
&self,
_request: crate::models::MessageRequest,
_request: codewhale_models::MessageRequest,
) -> anyhow::Result<crate::llm_client::StreamEventBox> {
use crate::llm_client::mock::canned;
if let Ok(mut requests) = self.requests.lock() {
@@ -21280,11 +21280,11 @@ impl crate::core::model_client::ModelClient for ReasoningOnlyCleanFinishModelCli
if call <= self.reasoning_only {
// A protocol-complete response that opened and closed only a
// thinking block: no text, no tool call, and a clean stop reason.
let events: Vec<anyhow::Result<crate::models::StreamEvent>> = vec![
let events: Vec<anyhow::Result<codewhale_models::StreamEvent>> = vec![
Ok(canned::message_start("reasoning_only_msg")),
Ok(StreamEvent::ContentBlockStart {
index: 0,
content_block: crate::models::ContentBlockStart::Thinking {
content_block: codewhale_models::ContentBlockStart::Thinking {
thinking: String::new(),
},
}),
@@ -21540,7 +21540,7 @@ async fn the_reasoning_only_nudge_rides_one_request_and_never_joins_the_session(
);
let nudge = crate::config::DEFAULT_REASONING_ONLY_REPROMPT_MESSAGE;
let carries_nudge = |messages: &Vec<crate::models::Message>| {
let carries_nudge = |messages: &Vec<codewhale_models::Message>| {
serde_json::to_string(messages)
.expect("messages serialize")
.contains(nudge)
@@ -21765,7 +21765,7 @@ async fn post_edit_hook_injects_diagnostics_message_before_next_request() {
assert_eq!(last.role, "user");
// turn_meta is now at the tail of the content array (PR #2517).
let meta = match last.content.last() {
Some(crate::models::ContentBlock::Text { text, .. }) => text.clone(),
Some(codewhale_models::ContentBlock::Text { text, .. }) => text.clone(),
other => panic!("expected text block at tail, got {other:?}"),
};
assert!(meta.starts_with("<turn_meta>\n"));
@@ -21773,7 +21773,7 @@ async fn post_edit_hook_injects_diagnostics_message_before_next_request() {
.content
.iter()
.find_map(|block| match block {
crate::models::ContentBlock::Text { text, .. }
codewhale_models::ContentBlock::Text { text, .. }
if text.contains("<diagnostics file=\"") =>
{
Some(text)
@@ -22980,7 +22980,7 @@ async fn background_completion_after_a_turn_is_delivered_once_on_the_next_turn()
// ...and it is model-visible, marked as untrusted tool data.
let message = crate::runtime_handoff::shell_completion_runtime_message(&first);
let crate::models::ContentBlock::Text { text, .. } = &message.content[0] else {
let codewhale_models::ContentBlock::Text { text, .. } = &message.content[0] else {
panic!("expected runtime event text");
};
assert!(text.contains("background_shell_completion"), "{text}");
@@ -1,6 +1,6 @@
//! Process-local memoization for [`crate::compaction::estimate_input_tokens_conservative`].
//!
//! The token estimator walks the full [`crate::models::Message`] history and the
//! The token estimator walks the full [`codewhale_models::Message`] history and the
//! active system prompt, which is by far the most expensive per-turn CPU cost
//! in the engine hot path. The same input data is queried from at least five
//! sites per turn: capacity pre/post tool checkpoints, error escalation,
@@ -21,7 +21,7 @@ use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use crate::compaction::estimate_input_tokens_conservative;
use crate::models::{Message, SystemPrompt};
use codewhale_models::{Message, SystemPrompt};
/// Default capacity for the rolling audit ring. Sized so a 64-entry window
/// covers a full capacity controller observation cycle without unbounded
@@ -166,8 +166,8 @@ fn fingerprint_system_prompt(system: Option<&SystemPrompt>) -> u64 {
#[cfg(test)]
mod tests {
use super::*;
use crate::models::Role;
use crate::models::{ContentBlock, SystemBlock};
use codewhale_models::Role;
use codewhale_models::{ContentBlock, SystemBlock};
fn user_text(s: &str) -> Message {
Message {
+1 -1
View File
@@ -14,10 +14,10 @@ use serde_json::{Value, json};
#[cfg(test)]
use crate::mcp::McpPool;
use crate::model_profile::ToolSurfaceBudget;
use crate::models::Tool;
use crate::tools::spec::{ToolError, ToolResult, optional_str, optional_u64, required_str};
use codewhale_config::AppMode;
use codewhale_execpolicy::ApprovalMode;
use codewhale_models::Tool;
use crate::core::session::ToolActivationCache;
use crate::dependencies::ExternalTool;
@@ -7,8 +7,8 @@ use super::{
touch_cached_tool_after_execution,
};
use crate::core::session::ToolActivationCache;
use crate::models::Tool;
use codewhale_config::AppMode;
use codewhale_models::Tool;
use serde_json::json;
use std::collections::{BTreeSet, HashSet};
+12 -11
View File
@@ -11,7 +11,6 @@ use super::dispatch::{
use super::*;
use crate::core::authority::{ToolPermission, resolve_tool_permission};
use crate::core::ops::UserInputProvenance;
use crate::models::Role;
use crate::prompt_zones::PinnedPrefix;
use crate::runtime_handoff::{
shell_completion_runtime_message, subagent_completion_runtime_message,
@@ -22,6 +21,7 @@ use crate::tools::canonical_action::canonical_action_alias;
use crate::tools::spec::ToolTerminalStatus;
use crate::tools::tool_call_budget::ToolCallBudget;
use codewhale_core::request::{PrimaryTurnRequest, prepare_primary_turn_request};
use codewhale_models::Role;
const MAX_APPROVAL_INTENT_SUMMARY_CHARS: usize = 2_000;
@@ -36,7 +36,7 @@ struct StreamOutcome {
current_text_visible: String,
current_thinking: String,
current_thinking_signature: Option<String>,
current_thinking_state: Option<crate::models::OpaqueReasoningState>,
current_thinking_state: Option<codewhale_models::OpaqueReasoningState>,
tool_uses: Vec<ToolUseState>,
usage: Usage,
usage_reported: bool,
@@ -1287,7 +1287,7 @@ impl Engine {
if let Some(pm) = self.session.prefix_stability.as_mut() {
let system_text =
crate::prefix_cache::system_prompt_text(self.session.system_prompt.as_ref());
let tools_ref: Option<&[crate::models::Tool]> = active_tools.as_deref();
let tools_ref: Option<&[codewhale_models::Tool]> = active_tools.as_deref();
let outcome = pm.check(&system_text, tools_ref, declared_change.as_deref());
// C5: request N's prefix may only diverge from N-1 across a
// DECLARED change. An undeclared drift means the pinned header
@@ -1382,7 +1382,8 @@ impl Engine {
// PrefixStabilityManager already reports the change above.
let system_text =
crate::prefix_cache::system_prompt_text(self.session.system_prompt.as_ref());
let current_tools: &[crate::models::Tool] = active_tools.as_deref().unwrap_or_default();
let current_tools: &[codewhale_models::Tool] =
active_tools.as_deref().unwrap_or_default();
match &self.session.frozen_prefix {
Some(frozen) => {
@@ -2645,7 +2646,7 @@ impl Engine {
stream_request.max_tokens
)
} else if has_provider_reasoning {
let reason = crate::models::stop_reason_detail(stop_reason.as_deref());
let reason = codewhale_models::stop_reason_detail(stop_reason.as_deref());
format!(
"Model returned reasoning but no answer or tool call; the provider response was incomplete (stop reason: {}).",
reason
@@ -2943,7 +2944,7 @@ impl Engine {
turn: &mut TurnContext,
tool_policy: &ToolSurfacePolicy,
tool_uses: &mut [ToolUseState],
tool_catalog: &[crate::models::Tool],
tool_catalog: &[codewhale_models::Tool],
tool_registry: Option<&crate::tools::ToolRegistry>,
active_tool_names: &mut std::collections::HashSet<String>,
tool_call_budget: &mut ToolCallBudget,
@@ -3581,7 +3582,7 @@ impl Engine {
plans: Vec<ToolExecutionPlan>,
origin_turn_id: &str,
current_text_visible: &str,
tool_catalog: &[crate::models::Tool],
tool_catalog: &[codewhale_models::Tool],
active_tool_names: &mut std::collections::HashSet<String>,
tool_registry: Option<&crate::tools::ToolRegistry>,
tool_exec_lock: Arc<RwLock<()>>,
@@ -4478,7 +4479,7 @@ impl Engine {
&mut self,
outcomes: Vec<Option<ToolExecOutcome>>,
turn: &mut TurnContext,
tool_catalog: &mut Vec<crate::models::Tool>,
tool_catalog: &mut Vec<codewhale_models::Tool>,
active_tool_names: &mut std::collections::HashSet<String>,
hook_contexts: &std::collections::HashMap<String, String>,
mut fleet_denial_guard: Option<&mut FleetDenialGuard>,
@@ -4729,7 +4730,7 @@ impl Engine {
&mut self,
client: &dyn crate::core::model_client::ModelClient,
stream: crate::llm_client::StreamEventBox,
stream_request: &crate::models::MessageRequest,
stream_request: &codewhale_models::MessageRequest,
mut request_dispatched_at: Instant,
drop_resumes_spent: u32,
diagnostics: &mut crate::tool_inspection::TurnStopDiagnostics,
@@ -4746,7 +4747,7 @@ impl Engine {
// #3014: Anthropic signed-thinking signature for the current
// thinking block; must be replayed verbatim in tool loops.
let mut current_thinking_signature: Option<String> = None;
let mut current_thinking_state: Option<crate::models::OpaqueReasoningState> = None;
let mut current_thinking_state: Option<codewhale_models::OpaqueReasoningState> = None;
let mut tool_uses: Vec<ToolUseState> = Vec::new();
let mut usage = Usage {
input_tokens: 0,
@@ -6735,7 +6736,7 @@ mod tests {
},
]);
let text = match &message.content[0] {
crate::models::ContentBlock::Text { text, .. } => text,
codewhale_models::ContentBlock::Text { text, .. } => text,
other => panic!("expected runtime event text, got {other:?}"),
};
assert!(text.contains("background_shell_completion"));
+1 -1
View File
@@ -10,11 +10,11 @@ use serde_json::Value;
use crate::config::ApiProvider;
use crate::error_taxonomy::ErrorEnvelope;
use crate::models::{Message, SystemPrompt, Tool, Usage};
use crate::tools::goal::GoalSnapshot;
use crate::tools::spec::{ToolError, ToolResult};
use crate::tools::subagent::{AgentWorkerStatus, CoordinationDetailProjection, SubAgentResult};
use crate::tools::user_input::UserInputRequest;
use codewhale_models::{Message, SystemPrompt, Tool, Usage};
/// Final status for a turn.
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
+1 -1
View File
@@ -5,11 +5,11 @@
use crate::compaction::CompactionConfig;
use crate::config::ApiProvider;
use crate::models::{Message, SystemPrompt};
use crate::route_runtime::ResolvedRuntimeRoute;
use crate::tools::goal::GoalStatus;
use codewhale_config::AppMode;
use codewhale_execpolicy::ApprovalMode;
use codewhale_models::{Message, SystemPrompt};
use codewhale_protocol::runtime::DynamicToolSpec;
use std::path::PathBuf;
+1 -1
View File
@@ -39,13 +39,13 @@ use crate::core::ops::Op;
use crate::cost_status::RouteBillingMode;
use crate::mcp::{McpManagerSnapshot, McpServerCapabilityMetadata};
use crate::model_profile::SupportState;
use crate::models::Usage;
use crate::route_billing::RouteProduct;
use crate::tools::spec::ToolError;
use crate::tools::subagent::AgentWorkerStatus;
use crate::tools::user_input::UserInputRequest;
use codewhale_config::AppMode;
use codewhale_execpolicy::ApprovalMode;
use codewhale_models::Usage;
use codewhale_protocol::ResponseChannel;
/// Routing ids the engine does not carry on each event; the emitter supplies
@@ -5,7 +5,7 @@ use async_trait::async_trait;
use crate::llm_client::LlmClient;
use crate::llm_client::StreamEventBox;
use crate::models::{MessageRequest, MessageResponse};
use codewhale_models::{MessageRequest, MessageResponse};
/// Object-safe model boundary for Engine dependency injection.
///
+9 -9
View File
@@ -2,12 +2,12 @@
//!
//! Tracks conversation history, token usage, and session metadata.
use crate::models::{Message, SystemPrompt, Usage};
use crate::prefix_cache::PrefixStabilityManager;
use crate::project_context::{ProjectContext, load_project_context_with_parents};
use crate::prompt_zones::{AppendLog, FrozenPrefix};
use crate::working_set::WorkingSet;
use codewhale_execpolicy::ApprovalMode;
use codewhale_models::{Message, SystemPrompt, Usage};
use std::collections::{HashSet, VecDeque};
use std::path::PathBuf;
@@ -48,19 +48,19 @@ impl ToolActivationCache {
}
fn catalog_tool<'a>(
catalog: &'a [crate::models::Tool],
catalog: &'a [codewhale_models::Tool],
name: &str,
) -> Option<&'a crate::models::Tool> {
) -> Option<&'a codewhale_models::Tool> {
catalog
.iter()
.find(|tool| tool.name == name && tool.defer_loading.unwrap_or(false))
}
fn serialized_bytes(tool: &crate::models::Tool) -> usize {
fn serialized_bytes(tool: &codewhale_models::Tool) -> usize {
serde_json::to_vec(tool).map_or(usize::MAX, |bytes| bytes.len())
}
fn total_serialized_bytes(&self, catalog: &[crate::models::Tool]) -> usize {
fn total_serialized_bytes(&self, catalog: &[codewhale_models::Tool]) -> usize {
self.names
.iter()
.filter_map(|name| Self::catalog_tool(catalog, name))
@@ -70,7 +70,7 @@ impl ToolActivationCache {
/// Drop entries that are no longer deferred members of this turn's
/// filtered catalog and enforce both cache bounds.
pub(crate) fn revalidate(&mut self, catalog: &[crate::models::Tool]) -> Vec<String> {
pub(crate) fn revalidate(&mut self, catalog: &[codewhale_models::Tool]) -> Vec<String> {
let mut evicted = Vec::new();
self.names.retain(|name| {
let keep = Self::catalog_tool(catalog, name).is_some_and(|tool| {
@@ -98,7 +98,7 @@ impl ToolActivationCache {
/// until both bounds hold.
pub(crate) fn activate(
&mut self,
catalog: &[crate::models::Tool],
catalog: &[codewhale_models::Tool],
requested: &[String],
) -> ToolActivationDelta {
let mut delta = ToolActivationDelta {
@@ -378,8 +378,8 @@ mod tests {
use super::*;
use serde_json::json;
fn deferred_tool(name: &str, description_bytes: usize) -> crate::models::Tool {
crate::models::Tool {
fn deferred_tool(name: &str, description_bytes: usize) -> codewhale_models::Tool {
codewhale_models::Tool {
tool_type: None,
name: name.to_string(),
description: "x".repeat(description_bytes),
+3 -3
View File
@@ -14,8 +14,8 @@
//! snapshots.
use crate::core::events::TurnRoute;
use crate::models::Usage;
use crate::snapshot::SnapshotRepo;
use codewhale_models::Usage;
use std::path::Path;
use std::time::{Duration, Instant};
@@ -295,7 +295,7 @@ fn add_optional_usage(total: Option<u32>, delta: Option<u32>) -> Option<u32> {
#[cfg(test)]
mod usage_tests {
use super::*;
use crate::models::ServerToolUsage;
use codewhale_models::ServerToolUsage;
#[test]
fn add_usage_preserves_replay_and_saturates_server_tool_counters() {
@@ -323,7 +323,7 @@ mod usage_tests {
assert_eq!(server.tool_search_requests, Some(5));
}
fn below_threshold(messages: &[crate::models::Message], turn: &TurnContext) -> bool {
fn below_threshold(messages: &[codewhale_models::Message], turn: &TurnContext) -> bool {
let config = crate::compaction::CompactionConfig {
enabled: true,
token_threshold: 100_000,
+1 -1
View File
@@ -38,9 +38,9 @@ use std::sync::{Arc, Mutex, OnceLock};
use chrono::{DateTime, Utc};
use crate::config::ApiProvider;
use crate::models::Usage;
use crate::pricing::{CostEstimate, TurnCostAudit};
use crate::route_billing::BillingPresentation;
use codewhale_models::Usage;
/// Everything a drained background accrual needs to be explained.
///
+4 -4
View File
@@ -54,8 +54,8 @@ use super::role::{
};
use crate::config::{ApiProvider, Config};
use crate::llm_client::LlmClient;
use crate::models::Role;
use crate::reasoning_preference::ReasoningEffort;
use codewhale_models::Role;
/// Where exact Fleet definitions and Reasoning Router profiles are looked up,
/// labelled so an identity can be qualified (`workspace/glm-pair`) instead of
@@ -452,7 +452,7 @@ impl FleetRouterCaller for LiveFleetRouter {
}
async fn decide(&self, input: &RouterCallInput) -> Result<String, String> {
use crate::models::{ContentBlock, Message, MessageRequest, SystemPrompt};
use codewhale_models::{ContentBlock, Message, MessageRequest, SystemPrompt};
// The bounded, redacted summary is transmitted exactly once, in the
// user turn. The system prompt carries the contract and the frozen
@@ -495,10 +495,10 @@ impl FleetRouterCaller for LiveFleetRouter {
.create_message(request)
.await
.map_err(|error| error.to_string())?;
if crate::models::is_incomplete_stop_reason(response.stop_reason.as_deref()) {
if codewhale_models::is_incomplete_stop_reason(response.stop_reason.as_deref()) {
return Err(format!(
"reasoning router response incomplete: provider stop reason `{}`",
crate::models::stop_reason_detail(response.stop_reason.as_deref())
codewhale_models::stop_reason_detail(response.stop_reason.as_deref())
));
}
let text = response
+4 -4
View File
@@ -905,7 +905,7 @@ pub struct TurnEndPayloadInput<'a> {
pub status: &'a str,
pub error: Option<&'a str>,
pub duration: Duration,
pub usage: &'a crate::models::Usage,
pub usage: &'a codewhale_models::Usage,
pub totals: TurnEndTotals,
pub tool_count: usize,
pub queued_message_count: usize,
@@ -3326,7 +3326,7 @@ NOEQUAL line dropped
.with_mode("agent")
.with_model("deepseek-v4")
.with_tokens(125);
let usage = crate::models::Usage {
let usage = codewhale_models::Usage {
input_tokens: 40,
output_tokens: 9,
prompt_cache_hit_tokens: Some(10),
@@ -3779,7 +3779,7 @@ printf '%s\n' '{{"text":"stdout is not a mutation contract"}}'
},
dir.path().to_path_buf(),
);
let usage = crate::models::Usage {
let usage = codewhale_models::Usage {
input_tokens: 12,
output_tokens: 3,
prompt_cache_hit_tokens: None,
@@ -5334,7 +5334,7 @@ command = "echo project"
#[test]
fn turn_end_error_is_sanitized_and_bounded() {
let context = HookContext::new();
let usage = crate::models::Usage::default();
let usage = codewhale_models::Usage::default();
let error = format!(
"boom\u{1b}[2J{}",
"x".repeat(super::HOOK_TURN_ERROR_MAX_CHARS * 2)
+4 -4
View File
@@ -53,7 +53,7 @@ use std::path::Path;
use base64::{Engine as _, engine::general_purpose::STANDARD};
use crate::model_profile::SupportState;
use crate::models::{ContentBlock, ImageUrlContent};
use codewhale_models::{ContentBlock, ImageUrlContent};
/// Largest source image accepted, in bytes, before base64 expansion.
///
@@ -401,8 +401,8 @@ pub(crate) fn safe_tool_result_content_blocks(
#[must_use]
pub(crate) fn safe_tool_result_message_projection(
messages: &[crate::models::Message],
) -> Vec<crate::models::Message> {
messages: &[codewhale_models::Message],
) -> Vec<codewhale_models::Message> {
let mut projected = messages.to_vec();
for message in &mut projected {
for block in &mut message.content {
@@ -641,7 +641,7 @@ fn tag_block(text: &str) -> ContentBlock {
/// The image is replaced in place rather than removed, so the model is told
/// why it is looking at a gap instead of being left to invent one.
pub fn strip_images_when_unsupported(
messages: &mut [crate::models::Message],
messages: &mut [codewhale_models::Message],
vision: SupportState,
model: &str,
) -> usize {
+5 -5
View File
@@ -1,5 +1,5 @@
use super::*;
use crate::models::Role;
use codewhale_models::Role;
/// A 1x1 PNG, as bytes rather than a fixture file so the encoding tests
/// have no filesystem dependency.
@@ -52,7 +52,7 @@ fn lowercase_read_image_preparation_is_typed_and_bounded() {
#[test]
fn blind_route_removes_nested_tool_result_image() {
let mut messages = vec![crate::models::Message {
let mut messages = vec![codewhale_models::Message {
role: Role::User,
content: vec![ContentBlock::ToolResult {
tool_use_id: "call-image".to_string(),
@@ -200,8 +200,8 @@ fn classifies_remote_urls() {
assert!(!is_remote_image_url("file:///tmp/a.png"));
}
fn message_with_image(url: &str) -> crate::models::Message {
crate::models::Message {
fn message_with_image(url: &str) -> codewhale_models::Message {
codewhale_models::Message {
role: Role::User,
content: vec![
ContentBlock::ImageUrl {
@@ -321,7 +321,7 @@ fn ingest_does_not_consult_model_capability() {
fn a_blind_route_gets_text_in_place_of_every_image() {
let mut messages = vec![
message_with_image("data:image/png;base64,QUJD"),
crate::models::Message {
codewhale_models::Message {
role: Role::Assistant,
content: vec![ContentBlock::Text {
text: "sure".to_string(),
+32 -33
View File
@@ -68,13 +68,11 @@ mod lsp;
mod mcp;
mod mcp_server;
mod media_originals;
mod model_catalog;
mod model_context;
mod model_inventory;
mod model_profile;
mod model_registry;
mod model_routing;
mod models;
mod models_dev_live;
mod native_memory;
mod network_policy;
@@ -168,11 +166,11 @@ use crate::mcp::{
McpCommandAvailability, McpConfig, McpPool, McpServerConfig, McpServerOAuthConfig,
is_relative_stdio_path_arg,
};
use crate::models::Role;
use crate::models::{ContentBlock, Message, MessageRequest, SystemPrompt};
use crate::session_manager::{SessionManager, create_saved_session, truncate_id};
use crate::tui::app::ScreenMode;
use crate::tui::history::{summarize_tool_args, summarize_tool_output};
use codewhale_models::Role;
use codewhale_models::{ContentBlock, Message, MessageRequest, SystemPrompt};
#[cfg(windows)]
fn configure_windows_console_utf8() {
@@ -7779,7 +7777,7 @@ mod speech_cli_tests {
/// Test API connectivity by making a minimal request
async fn test_api_connectivity(config: &Config) -> Result<()> {
use crate::client::DeepSeekClient;
use crate::models::{ContentBlock, Message, MessageRequest};
use codewhale_models::{ContentBlock, Message, MessageRequest};
let client = DeepSeekClient::new(config)?;
let model = client.model().to_string();
@@ -8386,24 +8384,25 @@ Provide findings ordered by severity with file references, then open questions,
let client = DeepSeekClient::new(&execution_config)?;
let request_route = client.effective_route_envelope(&model, chrono::Utc::now());
let planned_passes = prompts.len();
let mut usage = crate::models::Usage::default();
let mut usage = codewhale_models::Usage::default();
let mut publication = if args.post {
ReviewPublication::NotAttempted
} else {
ReviewPublication::NotRequested
};
let report_failure = |usage: &crate::models::Usage, completed_passes, publication, message| {
report_review_failure(
&args,
&route_provider,
&model,
usage,
completed_passes,
planned_passes,
publication,
message,
)
};
let report_failure =
|usage: &codewhale_models::Usage, completed_passes, publication, message| {
report_review_failure(
&args,
&route_provider,
&model,
usage,
completed_passes,
planned_passes,
publication,
message,
)
};
let mut accumulator = pr_plan
.as_ref()
.map(crate::tools::review::PrReviewAccumulator::new);
@@ -8450,7 +8449,7 @@ Provide findings ordered by severity with file references, then open questions,
};
crate::tools::review::add_review_usage(&mut usage, &response.usage);
review_stop_reason = response.stop_reason.clone();
if crate::models::is_incomplete_stop_reason(review_stop_reason.as_deref()) {
if codewhale_models::is_incomplete_stop_reason(review_stop_reason.as_deref()) {
return report_failure(
&usage,
index,
@@ -8459,7 +8458,7 @@ Provide findings ordered by severity with file references, then open questions,
"Review pass {}/{} incomplete: provider stop reason `{}`; the partial review was not accepted or posted.",
index + 1,
planned_passes,
crate::models::stop_reason_detail(review_stop_reason.as_deref())
codewhale_models::stop_reason_detail(review_stop_reason.as_deref())
),
);
}
@@ -8622,7 +8621,7 @@ impl ReviewPublication {
fn review_failure_payload(
provider: &str,
model: &str,
usage: &crate::models::Usage,
usage: &codewhale_models::Usage,
completed_passes: usize,
planned_passes: usize,
publication: ReviewPublication,
@@ -8646,7 +8645,7 @@ fn report_review_failure(
args: &ReviewArgs,
provider: &str,
model: &str,
usage: &crate::models::Usage,
usage: &codewhale_models::Usage,
completed_passes: usize,
planned_passes: usize,
publication: ReviewPublication,
@@ -11372,7 +11371,7 @@ async fn run_one_shot(
force_configured_route: bool,
) -> Result<()> {
use crate::client::DeepSeekClient;
use crate::models::{
use codewhale_models::{
ContentBlock, Message, MessageRequest, is_incomplete_stop_reason, stop_reason_detail,
};
@@ -11432,7 +11431,7 @@ async fn run_one_shot_json(
force_configured_route: bool,
) -> Result<()> {
use crate::client::DeepSeekClient;
use crate::models::{
use codewhale_models::{
ContentBlock, Message, MessageRequest, SystemPrompt, is_incomplete_stop_reason,
stop_reason_detail,
};
@@ -11502,13 +11501,13 @@ fn one_shot_exec_json_receipt(
model: String,
output: String,
stop_reason: Option<String>,
usage: crate::models::Usage,
usage: codewhale_models::Usage,
) -> serde_json::Value {
let incomplete = crate::models::is_incomplete_stop_reason(stop_reason.as_deref());
let incomplete = codewhale_models::is_incomplete_stop_reason(stop_reason.as_deref());
let error = incomplete.then(|| {
format!(
"Model response incomplete: provider stop reason `{}`.",
crate::models::stop_reason_detail(stop_reason.as_deref())
codewhale_models::stop_reason_detail(stop_reason.as_deref())
)
});
serde_json::json!({
@@ -15707,7 +15706,7 @@ api_key = "test-only-key"
#[test]
fn review_failure_payload_preserves_usage_without_complete_claim() {
let usage = crate::models::Usage {
let usage = codewhale_models::Usage {
input_tokens: 24,
output_tokens: 4,
reasoning_tokens: Some(3),
@@ -15735,15 +15734,15 @@ api_key = "test-only-key"
#[test]
fn review_failure_after_publication_preserves_all_usage_and_post_state() {
let mut usage = crate::models::Usage::default();
let mut usage = codewhale_models::Usage::default();
for response_usage in [
crate::models::Usage {
codewhale_models::Usage {
input_tokens: 21,
output_tokens: 5,
reasoning_tokens: Some(2),
..Default::default()
},
crate::models::Usage {
codewhale_models::Usage {
input_tokens: 34,
output_tokens: 8,
reasoning_tokens: Some(3),
@@ -16570,7 +16569,7 @@ api_key = "test-only-key"
"model-a".to_string(),
"done".to_string(),
Some("end_turn".to_string()),
crate::models::Usage {
codewhale_models::Usage {
input_tokens: 12,
output_tokens: 3,
..Default::default()
@@ -16584,7 +16583,7 @@ api_key = "test-only-key"
"model-a".to_string(),
"partial".to_string(),
Some("max_output_tokens".to_string()),
crate::models::Usage {
codewhale_models::Usage {
input_tokens: 20,
output_tokens: 9,
..Default::default()
+6 -6
View File
@@ -50,12 +50,12 @@ use anyhow::{Result, anyhow};
use async_stream::try_stream;
use futures_util::Stream;
use crate::models::{
use codewhale_models::{
ContentBlock, MessageDelta, MessageRequest, MessageResponse, StreamEvent, Usage,
};
use super::{LlmClient, StreamEventBox};
use crate::models::Role;
use codewhale_models::Role;
/// A pre-recorded "turn" the mock will replay on the next streaming call.
///
@@ -294,7 +294,7 @@ fn stream_from_canned(turn: CannedTurn) -> StreamEventBox {
/// `MessageResponse` by concatenating text deltas. Used only as a fallback
/// when callers `create_message` without a queued `MessageResponse`.
fn synthesize_message_response(turn: CannedTurn, model: &str) -> MessageResponse {
use crate::models::Delta;
use codewhale_models::Delta;
let mut text = String::new();
let mut stop_reason: Option<String> = None;
@@ -336,7 +336,7 @@ fn synthesize_message_response(turn: CannedTurn, model: &str) -> MessageResponse
pub mod canned {
use serde_json::Value;
use crate::models::{
use codewhale_models::{
ContentBlockStart, Delta, MessageDelta, MessageResponse, StreamEvent, Usage,
};
@@ -477,7 +477,7 @@ mod tests {
use super::*;
use crate::llm_client::LlmClient;
use crate::models::{Delta, Message, MessageRequest, StreamEvent};
use codewhale_models::{Delta, Message, MessageRequest, StreamEvent};
fn empty_request() -> MessageRequest {
MessageRequest {
@@ -641,7 +641,7 @@ mod tests {
while let Some(ev) = stream.next().await {
match ev.unwrap() {
StreamEvent::ContentBlockStart { content_block, .. } => {
use crate::models::ContentBlockStart;
use codewhale_models::ContentBlockStart;
if let ContentBlockStart::ToolUse { name, .. } = content_block {
assert_eq!(name, "list_dir");
saw_tool_use = true;
+1 -1
View File
@@ -23,8 +23,8 @@
//! ```
use crate::config::RetryPolicy;
use crate::models::{MessageRequest, MessageResponse, StreamEvent};
use anyhow::Result;
use codewhale_models::{MessageRequest, MessageResponse, StreamEvent};
use serde_json::Value;
use std::future::Future;
use std::pin::Pin;
+1 -1
View File
@@ -6,7 +6,7 @@ use std::sync::{Mutex, OnceLock};
use lru::LruCache;
use sha2::{Digest, Sha256};
use crate::models::{MessageRequest, MessageResponse, Usage};
use codewhale_models::{MessageRequest, MessageResponse, Usage};
const DEFAULT_CAPACITY: usize = 256;
+8 -8
View File
@@ -4049,11 +4049,11 @@ impl McpPool {
}
/// Convert discovered tools to API Tool format
pub fn to_api_tools(&self) -> Vec<crate::models::Tool> {
pub fn to_api_tools(&self) -> Vec<codewhale_models::Tool> {
let mut api_tools = Vec::new();
// Add regular tools
for (name, tool) in self.all_tools() {
api_tools.push(crate::models::Tool {
api_tools.push(codewhale_models::Tool {
tool_type: None,
name,
description: tool.description.clone().unwrap_or_default(),
@@ -4095,7 +4095,7 @@ impl McpPool {
if api_tools.iter().any(|tool| tool.name == name) {
continue;
}
api_tools.push(crate::models::Tool {
api_tools.push(codewhale_models::Tool {
tool_type: None,
name,
description: oauth::authenticate_tool_description(server),
@@ -4119,7 +4119,7 @@ impl McpPool {
// and prompt tokens. Gate each on its own non-empty collection, mirroring
// the `mcp_read_resource` guard below (`!resources.is_empty()`).
if !self.all_resources().is_empty() {
api_tools.push(crate::models::Tool {
api_tools.push(codewhale_models::Tool {
tool_type: None,
name: "list_mcp_resources".to_string(),
description: "List available MCP resources across servers (optionally filtered by server).".to_string(),
@@ -4137,7 +4137,7 @@ impl McpPool {
});
}
if !self.all_resource_templates().is_empty() {
api_tools.push(crate::models::Tool {
api_tools.push(codewhale_models::Tool {
tool_type: None,
name: "list_mcp_resource_templates".to_string(),
description: "List available MCP resource templates across servers (optionally filtered by server).".to_string(),
@@ -4158,7 +4158,7 @@ impl McpPool {
// Add resource reading tools if resources exist
let resources = self.all_resources();
if !resources.is_empty() {
api_tools.push(crate::models::Tool {
api_tools.push(codewhale_models::Tool {
tool_type: None,
name: "mcp_read_resource".to_string(),
description: "Read a resource from an MCP server using its URI".to_string(),
@@ -4176,7 +4176,7 @@ impl McpPool {
strict: None,
cache_control: None,
});
api_tools.push(crate::models::Tool {
api_tools.push(codewhale_models::Tool {
tool_type: None,
name: "read_mcp_resource".to_string(),
description: "Alias for mcp_read_resource.".to_string(),
@@ -4199,7 +4199,7 @@ impl McpPool {
// Add prompt getting tools if prompts exist
let prompts = self.all_prompts();
if !prompts.is_empty() {
api_tools.push(crate::models::Tool {
api_tools.push(codewhale_models::Tool {
tool_type: None,
name: "mcp_get_prompt".to_string(),
description: "Get a prompt from an MCP server".to_string(),
+4 -4
View File
@@ -14,11 +14,11 @@ use uuid::Uuid;
use crate::client::DeepSeekClient;
use crate::config::Config;
use crate::llm_client::LlmClient;
use crate::models::Role;
use crate::models::{ContentBlock, Message, MessageRequest};
use crate::session_manager::SessionManager;
use crate::tools::spec::{ToolError, ToolResult};
use crate::tools::{ToolContext, ToolRegistryBuilder};
use codewhale_models::Role;
use codewhale_models::{ContentBlock, Message, MessageRequest};
#[derive(Debug, Default, Deserialize)]
struct McpServerConfigFile {
@@ -427,10 +427,10 @@ impl McpServer {
// A provider-declared incomplete reply must not enter the stored
// thread or be returned as a successful answer. The billed usage is
// still reported in the error payload.
if crate::models::is_incomplete_stop_reason(response.stop_reason.as_deref()) {
if codewhale_models::is_incomplete_stop_reason(response.stop_reason.as_deref()) {
let error = format!(
"Model response incomplete: provider stop reason `{}`; the partial reply was not accepted.",
crate::models::stop_reason_detail(response.stop_reason.as_deref())
codewhale_models::stop_reason_detail(response.stop_reason.as_deref())
);
return Ok(json!({
"content": [{ "type": "text", "text": &error }],
+1 -1
View File
@@ -2,7 +2,7 @@
use std::collections::BTreeMap;
use crate::models::SystemBlock;
use codewhale_models::SystemBlock;
use super::fragment::{FragmentId, FragmentRender, FragmentRole, ModelContextFragment};
+1 -1
View File
@@ -135,7 +135,7 @@ impl ModelInventory {
// that nobody verified it — the auto-router must not read a
// guessed window as a route capability.
let mut context_window_unverified =
crate::model_catalog::resolved_context_window(&model).is_none();
codewhale_models::model_catalog::resolved_context_window(&model).is_none();
if let Ok(route) =
crate::route_runtime::resolve_runtime_route(config, provider, Some(&model))
{
+9 -9
View File
@@ -3,10 +3,10 @@
//! Historically, "what is this model's context window / max output / does it
//! reason?" was answered by several hard-coded sites:
//!
//! * [`crate::models::context_window_for_model`] /
//! * [`codewhale_models::context_window_for_model`] /
//! the models module's context-window lookup for context windows,
//! * [`crate::models::max_output_tokens_for_model`] for output caps,
//! * [`crate::models::model_supports_reasoning`] for the reasoning flag,
//! * [`codewhale_models::max_output_tokens_for_model`] for output caps,
//! * [`codewhale_models::model_supports_reasoning`] for the reasoning flag,
//! * the `DEFAULT_*` model-id constants in `crates/config/src/lib.rs` for the
//! canonical model each provider ships by default.
//!
@@ -20,7 +20,7 @@
//!
//! The registry does not re-declare context-window / max-output / reasoning
//! numbers. Instead it **seeds** each entry by calling the existing
//! `crate::models` functions, so the registry can never silently disagree with
//! `codewhale_models` functions, so the registry can never silently disagree with
//! `models.rs`. The canonical model ids come from the same provider defaults
//! the config crate ships (see [`SEED_MODEL_IDS`]). The
//! The `registry_context_window_matches_models_rs` drift guard then
@@ -34,7 +34,7 @@
use std::collections::BTreeMap;
use std::sync::OnceLock;
use crate::models::{
use codewhale_models::{
context_window_for_model, max_output_tokens_for_model, model_supports_reasoning,
};
@@ -84,7 +84,7 @@ pub enum ModelProvider {
/// One row of model facts, looked up in [`lookup`].
///
/// All numeric fields are seeded from `crate::models` so they stay in lockstep
/// All numeric fields are seeded from `codewhale_models` so they stay in lockstep
/// with the legacy lookups (see module docs).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ModelMetadata {
@@ -103,7 +103,7 @@ pub struct ModelMetadata {
impl ModelMetadata {
/// Build a metadata row for `id` by seeding every fact from the existing
/// `crate::models` lookups. This is the only constructor, which is what
/// `codewhale_models` lookups. This is the only constructor, which is what
/// keeps the registry from drifting away from `models.rs`.
fn seed(id: &'static str, provider: ModelProvider) -> Self {
Self {
@@ -238,7 +238,7 @@ fn registry() -> &'static BTreeMap<&'static str, ModelMetadata> {
///
/// Returns a pre-seeded [`ModelMetadata`] when `model` is one of the canonical
/// [`SEED_MODEL_IDS`] (case-insensitive). For any other id, this falls back to
/// the same `crate::models` heuristics (explicit `_Nk` suffix, DeepSeek/Claude
/// the same `codewhale_models` heuristics (explicit `_Nk` suffix, DeepSeek/Claude
/// family rules, etc.) and reports the provider as [`ModelProvider::Other`], so
/// callers always get a usable answer rather than `None` for a real model.
///
@@ -285,7 +285,7 @@ mod tests {
/// DRIFT GUARD (#3071, #3073).
///
/// The registry must agree with `crate::models` for the context window of
/// The registry must agree with `codewhale_models` for the context window of
/// every model it claims to know. Today they agree because the registry is
/// *seeded* from `models.rs`; this test exists so that if a future change
/// replaces a seed with a hard-coded literal that drifts from `models.rs`,
+9 -9
View File
@@ -15,9 +15,9 @@ use crate::cost_status::{
};
use crate::llm_client::LlmClient;
use crate::model_inventory::ModelInventory;
use crate::models::Role;
use crate::models::{ContentBlock, Message, MessageRequest, MessageResponse, SystemPrompt};
use crate::reasoning_preference::ReasoningEffort;
use codewhale_models::Role;
use codewhale_models::{ContentBlock, Message, MessageRequest, MessageResponse, SystemPrompt};
/// Big/cheap model pair the auto-router may choose between for the active
/// provider (#3018).
@@ -1038,7 +1038,7 @@ fn auto_route_pair(
AutoRoutePair { strong, fast }
}
fn auto_route_usage_has_reported_data(usage: &crate::models::Usage) -> bool {
fn auto_route_usage_has_reported_data(usage: &codewhale_models::Usage) -> bool {
usage.input_tokens > 0
|| usage.output_tokens > 0
|| usage.prompt_cache_hit_tokens.is_some()
@@ -1098,7 +1098,7 @@ fn auto_route_attempt_from_response(
.into_iter()
.collect();
let recommendation =
(!crate::models::is_incomplete_stop_reason(response.stop_reason.as_deref()))
(!codewhale_models::is_incomplete_stop_reason(response.stop_reason.as_deref()))
.then(|| {
parse_inventory_auto_route_recommendation(
&message_response_text(response),
@@ -1474,7 +1474,7 @@ mod tests {
id: &str,
text: &str,
stop_reason: &str,
usage: crate::models::Usage,
usage: codewhale_models::Usage,
) -> MessageResponse {
MessageResponse {
id: id.to_string(),
@@ -1553,7 +1553,7 @@ mod tests {
"same-provider-response-id",
&format!(r#"{{"provider":"openrouter","model":"{model}","thinking":"off"}}"#),
"stop",
crate::models::Usage {
codewhale_models::Usage {
input_tokens: 10,
output_tokens: 2,
prompt_cache_hit_tokens: Some(3),
@@ -1580,7 +1580,7 @@ mod tests {
"same-provider-response-id",
"not valid route json",
"stop",
crate::models::Usage {
codewhale_models::Usage {
input_tokens: 11,
output_tokens: 3,
..Default::default()
@@ -1594,7 +1594,7 @@ mod tests {
"same-provider-response-id",
&format!(r#"{{"provider":"openrouter","model":"{model}"}}"#),
"length",
crate::models::Usage {
codewhale_models::Usage {
input_tokens: 12,
output_tokens: 4,
..Default::default()
@@ -1610,7 +1610,7 @@ mod tests {
"missing-usage-response-id",
"not valid route json",
"stop",
crate::models::Usage::default(),
codewhale_models::Usage::default(),
),
&inventory,
);
+4 -1
View File
@@ -165,7 +165,10 @@ pub fn cache_path() -> Option<PathBuf> {
#[must_use]
pub fn status() -> ModelsDevStatus {
let current = STATUS.read().map(|guard| guard.clone()).unwrap_or_default();
honor_bundled_staleness(current, crate::model_catalog::bundled_catalog_is_stale())
honor_bundled_staleness(
current,
codewhale_models::model_catalog::bundled_catalog_is_stale(),
)
}
/// A Bundled-only report whose snapshot is itself past TTL reports `Stale`:
+1 -1
View File
@@ -41,7 +41,7 @@ use std::hash::{Hash, Hasher};
use serde::{Deserialize, Serialize};
use crate::models::{SystemPrompt, Tool};
use codewhale_models::{SystemPrompt, Tool};
/// A snapshot of the immutable prefix's fingerprint.
///
+12 -12
View File
@@ -16,7 +16,7 @@ use crate::config::{
DEFAULT_STEPFUN_BASE_URL, DEFAULT_STEPFUN_MODEL, DEFAULT_STEPFUN_PLAN_BASE_URL,
canonical_model_id_for_provider,
};
use crate::models::{Usage, has_date_snapshot_suffix};
use codewhale_models::{Usage, has_date_snapshot_suffix};
/// Cost display currency.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -747,7 +747,7 @@ fn known_pricing_for_model(model_lower: &str) -> Option<ModelPricing> {
return explicit;
}
if let Some((input_usd_per_million, output_usd_per_million)) =
crate::model_catalog::resolved_usd_pricing(model_lower)
codewhale_models::model_catalog::resolved_usd_pricing(model_lower)
{
return Some(usd_only_pricing(
input_usd_per_million,
@@ -4253,15 +4253,15 @@ mod tests {
#[test]
fn deepseek_pricing_requires_exact_ids_or_explicit_route_aliases() {
let _lock = crate::model_catalog::test_catalog_lock();
let _lock = codewhale_models::model_catalog::test_catalog_lock();
let at = utc_hm(2, 0);
let catalog = crate::model_catalog::MergedCatalog::from_sources(
let catalog = codewhale_models::model_catalog::MergedCatalog::from_sources(
BTreeMap::new(),
None,
crate::model_catalog::bundled_catalog(),
codewhale_models::model_catalog::bundled_catalog(),
at,
);
let _guard = crate::model_catalog::replace_active_catalog_for_test(catalog);
let _guard = codewhale_models::model_catalog::replace_active_catalog_for_test(catalog);
let usage = Usage {
input_tokens: 1_000,
output_tokens: 100,
@@ -4478,7 +4478,7 @@ mod tests {
#[test]
fn catalog_pricing_overrides_known_row_when_present() {
let _lock = crate::model_catalog::test_catalog_lock();
let _lock = codewhale_models::model_catalog::test_catalog_lock();
let mut overrides = BTreeMap::new();
let models = [
"catalog-priced-model",
@@ -4487,7 +4487,7 @@ mod tests {
for model in models {
overrides.insert(
model.to_string(),
crate::model_catalog::CatalogEntry {
codewhale_models::model_catalog::CatalogEntry {
id: model.to_string(),
context_window: None,
max_output: None,
@@ -4497,17 +4497,17 @@ mod tests {
modalities: Vec::new(),
supported_parameters: Vec::new(),
provider_model_id: None,
provenance: crate::model_catalog::MetadataProvenance::UserOverride,
provenance: codewhale_models::model_catalog::MetadataProvenance::UserOverride,
},
);
}
let catalog = crate::model_catalog::MergedCatalog::from_sources(
let catalog = codewhale_models::model_catalog::MergedCatalog::from_sources(
overrides,
None,
crate::model_catalog::bundled_catalog(),
codewhale_models::model_catalog::bundled_catalog(),
Utc::now(),
);
let _guard = crate::model_catalog::replace_active_catalog_for_test(catalog);
let _guard = codewhale_models::model_catalog::replace_active_catalog_for_test(catalog);
for model in models {
let pricing = pricing_for_model_at(model, Utc::now()).expect(model);
+7 -7
View File
@@ -21,8 +21,8 @@
//! `AppendLog` / `TurnScratch` / `ThreeZoneRequest` are type scaffolding
//! for future phases — not yet wired into the request path.
use crate::models::Role;
use crate::models::{Message, SystemPrompt, Tool};
use codewhale_models::Role;
use codewhale_models::{Message, SystemPrompt, Tool};
// ── helpers ────────────────────────────────────────────────────────────
fn sha256_hex(bytes: &[u8]) -> String {
@@ -348,16 +348,16 @@ impl<'a> ThreeZoneRequest<'a> {
Some(SystemPrompt::Text(text)) => {
messages.push(Message {
role: Role::System,
content: vec![crate::models::ContentBlock::Text {
content: vec![codewhale_models::ContentBlock::Text {
text: text.clone(),
cache_control: None,
}],
});
}
Some(SystemPrompt::Blocks(blocks)) => {
let content: Vec<crate::models::ContentBlock> = blocks
let content: Vec<codewhale_models::ContentBlock> = blocks
.iter()
.map(|block| crate::models::ContentBlock::Text {
.map(|block| codewhale_models::ContentBlock::Text {
text: block.text.clone(),
cache_control: block.cache_control.clone(),
})
@@ -398,7 +398,7 @@ impl<'a> ThreeZoneRequest<'a> {
#[cfg(test)]
mod tests {
use super::*;
use crate::models::ContentBlock;
use codewhale_models::ContentBlock;
fn make_tool(name: &str) -> Tool {
Tool {
@@ -652,7 +652,7 @@ mod tests {
#[test]
fn blocks_system_prompt_preserves_cache_control() {
use crate::models::{CacheControl, SystemBlock};
use codewhale_models::{CacheControl, SystemBlock};
let cc = Some(CacheControl {
cache_type: "ephemeral".to_string(),
});
+1 -1
View File
@@ -10,9 +10,9 @@
//! Keeping every layer's text in one module makes prompt tuning a
//! single-file operation.
use crate::models::{SystemBlock, SystemPrompt};
use crate::project_context::load_project_context_with_parents;
use codewhale_config::AppMode;
use codewhale_models::{SystemBlock, SystemPrompt};
use std::path::{Path, PathBuf};
use std::sync::{LazyLock, Mutex};
+1 -1
View File
@@ -28,7 +28,7 @@
use std::path::Path;
use crate::models::{SystemBlock, SystemPrompt};
use codewhale_models::{SystemBlock, SystemPrompt};
/// Pager title for the preview surface.
pub const PREVIEW_TITLE: &str = "Effective Base Prompt";
+5 -5
View File
@@ -14,9 +14,9 @@ use crate::config::ApiProvider;
use crate::core::events::Event;
use crate::fast_hash::{FastHashMap, FastHashSet};
use crate::llm_client::LlmClient;
use crate::models::Role;
use crate::models::{ContentBlock, Message, MessageRequest, Tool};
use crate::regex_cache::compile_user_regex;
use codewhale_models::Role;
use codewhale_models::{ContentBlock, Message, MessageRequest, Tool};
// ── Prompt‑building constants ──────────────────────────────────────────────
@@ -705,10 +705,10 @@ pub async fn run_purge(
// A truncated response can still carry a complete-looking `purge_context`
// call; executing it would mutate the session from incomplete output.
if crate::models::is_incomplete_stop_reason(response.stop_reason.as_deref()) {
if codewhale_models::is_incomplete_stop_reason(response.stop_reason.as_deref()) {
return Err(format!(
"Purge model response incomplete: provider stop reason `{}`; no purge was applied.",
crate::models::stop_reason_detail(response.stop_reason.as_deref())
codewhale_models::stop_reason_detail(response.stop_reason.as_deref())
));
}
@@ -1093,7 +1093,7 @@ mod tests {
}
use crate::llm_client::mock::MockLlmClient;
use crate::models::{MessageResponse, Usage};
use codewhale_models::{MessageResponse, Usage};
fn msg_response_with_tool_call(operations: serde_json::Value) -> MessageResponse {
MessageResponse {
+7 -6
View File
@@ -19,9 +19,10 @@ use serde_json::{Value, json};
use sha2::{Digest, Sha256};
use tokio::sync::mpsc;
use codewhale_models::{ContentBlock, Message};
use crate::{
core::events::{Event as EngineEvent, TurnOutcomeStatus},
models::{ContentBlock, Message},
runtime_chat_relay::{
RuntimeChatControlScope, RuntimeChatProjection, RuntimeChatPrompt, RuntimeChatRelayHost,
},
@@ -5004,7 +5005,7 @@ fn epoch_seconds() -> u64 {
#[cfg(test)]
mod tests {
use super::*;
use crate::models::Role;
use codewhale_models::Role;
use std::sync::{
Arc, Mutex,
atomic::{AtomicUsize, Ordering},
@@ -7697,8 +7698,8 @@ mod tests {
fn turn_complete_event() -> EngineEvent {
EngineEvent::TurnComplete {
usage: crate::models::Usage::default(),
parent_route_usage: crate::models::Usage::default(),
usage: codewhale_models::Usage::default(),
parent_route_usage: codewhale_models::Usage::default(),
routed_usage_dropped_records: 0,
status: TurnOutcomeStatus::Completed,
error: None,
@@ -8179,10 +8180,10 @@ mod tests {
assert!(controller.has_active_run());
assert!(controller.start(fixture_start()).is_err());
let usage = crate::models::Usage {
let usage = codewhale_models::Usage {
input_tokens: 17,
output_tokens: 5,
..crate::models::Usage::default()
..codewhale_models::Usage::default()
};
controller.observe_engine_event(&EngineEvent::TurnComplete {
usage: usage.clone(),
+4 -4
View File
@@ -21,13 +21,13 @@ use tokio::sync::Mutex;
use uuid::Uuid;
use crate::llm_client::LlmClient;
use crate::models::Role;
use crate::models::{
use crate::repl::runtime::{BatchResp, RpcDispatcher, RpcRequest, RpcResponse, SingleResp};
use crate::utils::spawn_supervised;
use codewhale_models::Role;
use codewhale_models::{
ContentBlock, Message, MessageRequest, MessageResponse, SystemPrompt, Usage,
is_incomplete_stop_reason, stop_reason_detail,
};
use crate::repl::runtime::{BatchResp, RpcDispatcher, RpcRequest, RpcResponse, SingleResp};
use crate::utils::spawn_supervised;
/// One pre-dispatch reservation in the shared routed-usage ledger.
#[derive(Debug, Clone, Copy)]
+3 -3
View File
@@ -22,7 +22,7 @@
//! - Code rounds and sub-LLM calls travel over a single stdin/stdout
//! pipe to a long-lived Python subprocess. No HTTP sidecar.
use crate::models::Usage;
use codewhale_models::Usage;
pub mod bridge;
pub mod prompt;
@@ -92,7 +92,7 @@ mod tests {
prompt_cache_miss_tokens: Some(20),
reasoning_tokens: Some(4),
reasoning_replay_tokens: Some(3),
server_tool_use: Some(crate::models::ServerToolUsage {
server_tool_use: Some(codewhale_models::ServerToolUsage {
code_execution_requests: Some(2),
tool_search_requests: Some(1),
}),
@@ -109,7 +109,7 @@ mod tests {
assert_eq!(total.reasoning_replay_tokens, Some(3));
assert_eq!(
total.server_tool_use,
Some(crate::models::ServerToolUsage {
Some(codewhale_models::ServerToolUsage {
code_execution_requests: Some(2),
tool_search_requests: Some(1),
})
+1 -1
View File
@@ -4,7 +4,7 @@
//! The prompt is deliberately strict: the only way to make progress is
//! through a `repl` block. There is no fall-through prose path.
use crate::models::SystemPrompt;
use codewhale_models::SystemPrompt;
/// Build the system prompt for a Recursive Language Model (RLM) root call.
pub fn rlm_system_prompt() -> SystemPrompt {
+2 -2
View File
@@ -10,8 +10,8 @@ use serde_json::{Value, json};
use tokio::sync::Mutex;
use uuid::Uuid;
use crate::models::{ContentBlock, Message, SystemPrompt};
use crate::repl::PythonRuntime;
use codewhale_models::{ContentBlock, Message, SystemPrompt};
pub type SharedRlmSessionStore = Arc<Mutex<HashMap<String, Arc<Mutex<RlmSession>>>>>;
@@ -459,7 +459,7 @@ fn sha256_hex(bytes: &[u8]) -> String {
#[cfg(test)]
mod tests {
use super::*;
use crate::models::Role;
use codewhale_models::Role;
#[test]
fn derive_session_name_slugifies_path() {
+4 -4
View File
@@ -10,15 +10,15 @@ use uuid::Uuid;
use crate::client::DeepSeekClient;
use crate::core::events::Event;
use crate::models::{
use crate::repl::PythonRuntime;
use codewhale_models::{
ContentBlock, Message, MessageRequest, SystemPrompt, Usage, is_incomplete_stop_reason,
stop_reason_detail,
};
use crate::repl::PythonRuntime;
use super::bridge::{RlmBridge, RlmLlmClient, RlmUsageAccumulator};
use super::prompt::rlm_system_prompt;
use crate::models::Role;
use codewhale_models::Role;
// ---------------------------------------------------------------------------
// Constants
@@ -998,7 +998,7 @@ fn truncate_text(text: &str, max_chars: usize) -> String {
mod tests {
use super::*;
use crate::llm_client::mock::MockLlmClient;
use crate::models::MessageResponse;
use codewhale_models::MessageResponse;
#[tokio::test]
async fn max_tokens_complete_repl_is_not_executed_or_accepted() {
+4 -4
View File
@@ -811,14 +811,14 @@ pub fn usage_chip(
provider,
model,
surface,
&crate::models::Usage::default(),
&codewhale_models::Usage::default(),
chrono::Utc::now(),
)
} else {
crate::pricing::audit_turn_cost_for_provider_at(
provider,
model,
&crate::models::Usage::default(),
&codewhale_models::Usage::default(),
chrono::Utc::now(),
)
};
@@ -1102,8 +1102,8 @@ fn xiaomi_is_explicit_pay_as_you_go(config: Option<&ProviderConfig>) -> bool {
#[cfg(test)]
mod tests {
use super::*;
use crate::models::Usage;
use crate::pricing::CostCurrency;
use codewhale_models::Usage;
fn config_with(provider: ApiProvider, provider_config: ProviderConfig) -> Config {
let mut config = Config::default();
@@ -1754,7 +1754,7 @@ mod tests {
fn child_route_billing_fails_closed_for_every_ambiguous_provider() {
use crate::pricing::UnpricedReason;
let usage = crate::models::Usage {
let usage = codewhale_models::Usage {
input_tokens: 10_000,
output_tokens: 1_000,
..Default::default()
+14 -14
View File
@@ -2,7 +2,7 @@ use codewhale_config::route::RouteLimits;
use crate::config::{ApiProvider, provider_capability};
use crate::context_budget::ContextBudget;
use crate::models::{DEFAULT_COMPACTION_TOKEN_THRESHOLD, context_window_for_model};
use codewhale_models::{DEFAULT_COMPACTION_TOKEN_THRESHOLD, context_window_for_model};
/// Safe ordinary API request cap across provider routes.
const API_MAX_OUTPUT_TOKENS: u32 = 65_536;
@@ -118,7 +118,7 @@ pub(crate) fn effective_max_output_tokens(model: &str) -> u32 {
// output ceiling in a machine-readable form; that number remains a
// catalogue-sourced value to re-verify against official docs when they
// publish one (#5373).
if let Some(documented) = crate::models::max_output_tokens_for_model(model) {
if let Some(documented) = codewhale_models::max_output_tokens_for_model(model) {
return documented.min(API_MAX_OUTPUT_TOKENS);
}
@@ -224,7 +224,7 @@ pub(crate) fn output_ceiling_source(provider: ApiProvider, model: &str) -> Outpu
if matches!(
provider,
ApiProvider::Anthropic | ApiProvider::MinimaxAnthropic | ApiProvider::Openmodel
) && crate::models::max_output_tokens_for_model(model).is_none()
) && codewhale_models::max_output_tokens_for_model(model).is_none()
{
return OutputCeilingSource::Unverified(ANTHROPIC_UNKNOWN_MAX_OUTPUT_TOKENS);
}
@@ -256,7 +256,7 @@ pub(crate) fn effective_max_output_tokens_for_route(
// for a configured 32K Ollama route, leaving just 1K for input (#5820).
// Explicit requests and documented ceilings retain their existing rules.
let requested_cap = if explicit_max_output_tokens_override().is_none()
&& crate::models::max_output_tokens_for_model(model).is_none()
&& codewhale_models::max_output_tokens_for_model(model).is_none()
&& route_cap.is_none()
&& matches!(
compatibility_source,
@@ -374,7 +374,7 @@ mod tests {
let _canonical = crate::test_support::EnvVarGuard::remove("CODEWHALE_MAX_OUTPUT_TOKENS");
let _legacy = crate::test_support::EnvVarGuard::remove("DEEPSEEK_MAX_OUTPUT_TOKENS");
let model = "qwen2.5:7b";
assert!(crate::models::max_output_tokens_for_model(model).is_none());
assert!(codewhale_models::max_output_tokens_for_model(model).is_none());
for provider in [
ApiProvider::Ollama,
ApiProvider::Sglang,
@@ -846,14 +846,14 @@ mod tests {
let _env_lock = crate::test_support::lock_test_env();
let _codewhale = crate::test_support::EnvVarGuard::remove("CODEWHALE_MAX_OUTPUT_TOKENS");
let _deepseek = crate::test_support::EnvVarGuard::remove("DEEPSEEK_MAX_OUTPUT_TOKENS");
let _catalog_lock = crate::model_catalog::test_catalog_lock();
let catalog = crate::model_catalog::MergedCatalog::from_sources(
let _catalog_lock = codewhale_models::model_catalog::test_catalog_lock();
let catalog = codewhale_models::model_catalog::MergedCatalog::from_sources(
std::collections::BTreeMap::new(),
None,
crate::model_catalog::bundled_catalog(),
codewhale_models::model_catalog::bundled_catalog(),
chrono::Utc::now(),
);
let _catalog = crate::model_catalog::replace_active_catalog_for_test(catalog);
let _catalog = codewhale_models::model_catalog::replace_active_catalog_for_test(catalog);
for provider in [
ApiProvider::Deepseek,
@@ -887,7 +887,7 @@ mod tests {
.map(|(id, max_output)| {
(
id.to_string(),
crate::model_catalog::CatalogEntry {
codewhale_models::model_catalog::CatalogEntry {
id: id.to_string(),
context_window: Some(128_000),
max_output: Some(max_output),
@@ -897,19 +897,19 @@ mod tests {
modalities: Vec::new(),
supported_parameters: Vec::new(),
provider_model_id: None,
provenance: crate::model_catalog::MetadataProvenance::UserOverride,
provenance: codewhale_models::model_catalog::MetadataProvenance::UserOverride,
},
)
})
.into_iter()
.collect();
let catalog = crate::model_catalog::MergedCatalog::from_sources(
let catalog = codewhale_models::model_catalog::MergedCatalog::from_sources(
overrides,
None,
crate::model_catalog::bundled_catalog(),
codewhale_models::model_catalog::bundled_catalog(),
chrono::Utc::now(),
);
let _override = crate::model_catalog::replace_active_catalog_for_test(catalog);
let _override = codewhale_models::model_catalog::replace_active_catalog_for_test(catalog);
for (model, expected) in [
("deepseek-v4.1-flash-expires-on-0910", 24_576),
("deepseek-v4-flash", 32_768),
+2 -2
View File
@@ -12,7 +12,7 @@ use crate::config::{
ProviderIdentity, is_exact_direct_moonshot_k3_route, is_exact_kimi_code_bare_k3_route,
validate_kimi_code_api_model_id,
};
use crate::models::DIRECT_KIMI_K3_MAX_OUTPUT_TOKENS;
use codewhale_models::DIRECT_KIMI_K3_MAX_OUTPUT_TOKENS;
/// Why a route is using its effective context-window value. Keep this
/// receipt separate from the numeric route limits so every consumer can state
@@ -162,7 +162,7 @@ pub(crate) fn resolve_context_window(
/// (a hint that overstates the window delays compaction past the provider's
/// real limit).
fn classify_capability_fallback_window(model: &str, tokens: u32) -> ContextWindowSource {
if crate::models::name_suffix_context_window_hint(model) == Some(tokens) {
if codewhale_models::name_suffix_context_window_hint(model) == Some(tokens) {
ContextWindowSource::NameSuffixHint
} else {
ContextWindowSource::Fallback
+2 -2
View File
@@ -65,8 +65,6 @@ use crate::fleet::profile::canonical_public_role_name;
use crate::fleet::task_spec::FleetTaskSpecDocument;
use crate::fleet::worker_runtime::fleet_write_roots;
use crate::mcp::McpPool;
#[cfg(test)]
pub(super) use crate::models::{ContentBlock, Message};
use crate::runtime_threads::{
CompactThreadRequest, CreateThreadRequest, ExternalApprovalDecision,
MAX_RUNTIME_EVENT_REPLAY_TAIL, RuntimeThreadManager, RuntimeThreadManagerConfig,
@@ -86,6 +84,8 @@ use crate::tools::subagent::{
AgentWorkerRecord, SharedSubAgentManager, load_persisted_agent_worker_records,
new_shared_subagent_manager_with_timeout,
};
#[cfg(test)]
pub(super) use codewhale_models::{ContentBlock, Message};
use codewhale_protocol::fleet::{
FleetArtifactKind, FleetEventReplay, FleetRun, FleetRunId, FleetRuntimeEvent,
FleetRuntimeTarget, FleetSecurityPolicy, FleetTaskSpec, FleetWorkerEventPayload,
+10 -10
View File
@@ -7,7 +7,6 @@ use axum::http::StatusCode;
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
use crate::models::{ContentBlock, Message};
use crate::runtime_threads::{
CreateThreadRequest, RuntimeTurnStatus, ThreadDetail, ThreadListFilter, TurnItemKind,
TurnItemLifecycleStatus,
@@ -18,9 +17,10 @@ use crate::session_manager::{
};
use crate::session_peek::{MAX_PEEK_ENTRIES, SessionPeek, build_peek};
use crate::session_projection::{SessionQuery, SessionSortMode, SessionSummary, project_sessions};
use codewhale_models::{ContentBlock, Message};
use super::{ApiError, RuntimeApiState, map_thread_err, truncate_text};
use crate::models::Role;
use codewhale_models::Role;
#[derive(Debug, Serialize)]
pub(super) struct SessionsResponse {
@@ -895,13 +895,13 @@ pub(super) fn session_to_detail(session: SavedSession) -> SessionDetailResponse
.content
.iter()
.map(|block| match block {
crate::models::ContentBlock::Text { text, .. } => {
codewhale_models::ContentBlock::Text { text, .. } => {
json!({ "type": "text", "text": text })
}
crate::models::ContentBlock::Thinking { thinking, .. } => {
codewhale_models::ContentBlock::Thinking { thinking, .. } => {
json!({ "type": "thinking", "text": thinking })
}
crate::models::ContentBlock::ToolUse {
codewhale_models::ContentBlock::ToolUse {
id,
name,
input,
@@ -913,7 +913,7 @@ pub(super) fn session_to_detail(session: SavedSession) -> SessionDetailResponse
}
obj
}
crate::models::ContentBlock::ToolResult {
codewhale_models::ContentBlock::ToolResult {
tool_use_id,
content,
is_error,
@@ -934,22 +934,22 @@ pub(super) fn session_to_detail(session: SavedSession) -> SessionDetailResponse
}
obj
}
crate::models::ContentBlock::ServerToolUse { id, name, input } => {
codewhale_models::ContentBlock::ServerToolUse { id, name, input } => {
json!({ "type": "tool_use", "id": id, "name": name, "input": input })
}
crate::models::ContentBlock::ToolSearchToolResult {
codewhale_models::ContentBlock::ToolSearchToolResult {
tool_use_id,
content,
} => {
json!({ "type": "tool_result", "tool_use_id": tool_use_id, "content": content })
}
crate::models::ContentBlock::CodeExecutionToolResult {
codewhale_models::ContentBlock::CodeExecutionToolResult {
tool_use_id,
content,
} => {
json!({ "type": "tool_result", "tool_use_id": tool_use_id, "content": content })
}
crate::models::ContentBlock::ImageUrl { .. } => Value::Null,
codewhale_models::ContentBlock::ImageUrl { .. } => Value::Null,
})
.collect();
json!({
+12 -12
View File
@@ -1,11 +1,11 @@
use super::*;
use crate::core::events::{Event as EngineEvent, TurnOutcomeStatus};
use crate::core::ops::Op;
use crate::models::Role;
use crate::models::Usage;
use crate::runtime_threads::RuntimeEventRecord;
use crate::test_support::{EnvVarGuard, lock_test_env};
use anyhow::{Context, bail};
use codewhale_models::Role;
use codewhale_models::Usage;
use futures_util::StreamExt;
use std::fs;
use std::path::Path;
@@ -271,7 +271,7 @@ impl crate::task_manager::TaskExecutor for MockExecutor {
}
}
fn saved_session_with_blocks(blocks: Vec<crate::models::ContentBlock>) -> SavedSession {
fn saved_session_with_blocks(blocks: Vec<codewhale_models::ContentBlock>) -> SavedSession {
SavedSession {
schema_version: 1,
metadata: SessionMetadata {
@@ -296,7 +296,7 @@ fn saved_session_with_blocks(blocks: Vec<crate::models::ContentBlock>) -> SavedS
},
journal: None,
leaf_id: None,
messages: vec![crate::models::Message {
messages: vec![codewhale_models::Message {
role: Role::Assistant,
content: blocks,
}],
@@ -366,11 +366,11 @@ fn session_detail_scenario() {
// from session_detail_tool_use_preserves_caller_metadata
{
let detail = session_to_detail(saved_session_with_blocks(vec![
crate::models::ContentBlock::ToolUse {
codewhale_models::ContentBlock::ToolUse {
id: "tool-1".to_string(),
name: "task_shell_start".to_string(),
input: json!({ "cmd": "cargo test" }),
caller: Some(crate::models::ToolCaller {
caller: Some(codewhale_models::ToolCaller {
caller_type: "subagent".to_string(),
tool_id: Some("parent-tool".to_string()),
}),
@@ -386,7 +386,7 @@ fn session_detail_scenario() {
// from session_detail_tool_result_keeps_fallback_content_with_blocks
{
let detail = session_to_detail(saved_session_with_blocks(vec![
crate::models::ContentBlock::ToolResult {
codewhale_models::ContentBlock::ToolResult {
tool_use_id: "tool-1".to_string(),
content: "fallback text".to_string(),
is_error: Some(false),
@@ -5367,16 +5367,16 @@ async fn spawn_server_with_saved_sessions(
let mut saved = crate::session_manager::create_saved_session_with_id_and_mode(
(*id).to_string(),
&[
crate::models::Message {
codewhale_models::Message {
role: Role::User,
content: vec![crate::models::ContentBlock::Text {
content: vec![codewhale_models::ContentBlock::Text {
text: format!("prompt for {title} with token=hunter2"),
cache_control: None,
}],
},
crate::models::Message {
codewhale_models::Message {
role: Role::Assistant,
content: vec![crate::models::ContentBlock::Text {
content: vec![codewhale_models::ContentBlock::Text {
text: "acknowledged".to_string(),
cache_control: None,
}],
@@ -13734,7 +13734,7 @@ fn runtime_image_named_catalog_cursor_binds_identity_endpoint_and_catalog() -> R
#[tokio::test]
async fn runtime_image_saved_session_import_validates_before_creating_a_thread() -> Result<()> {
use crate::models::{ContentBlock, ImageUrlContent};
use codewhale_models::{ContentBlock, ImageUrlContent};
let _env = crate::test_support::lock_test_env();
let dir = tempfile::tempdir()?;
let _home = crate::test_support::EnvVarGuard::set("CODEWHALE_HOME", dir.path());
+3 -3
View File
@@ -6,10 +6,10 @@
//! restart. This module owns both the exact live envelope and the narrow,
//! idempotent restore projection so creation and recognition cannot drift.
use crate::models::Role;
use crate::models::{ContentBlock, Message};
use crate::safe_label::SafeLabel;
use crate::tools::subagent::{AgentWorkerStatus, SubAgentResult, SubAgentStatus};
use codewhale_models::Role;
use codewhale_models::{ContentBlock, Message};
use serde::{Deserialize, Serialize};
const COMPLETION_EVENT_PREFIX: &str = concat!(
@@ -1448,7 +1448,7 @@ mod tests {
let image_only = Message {
role: Role::User,
content: vec![ContentBlock::ImageUrl {
image_url: crate::models::ImageUrlContent {
image_url: codewhale_models::ImageUrlContent {
url: "data:image/png;base64,AAAA".to_string(),
},
}],
+2 -2
View File
@@ -40,8 +40,6 @@ use crate::cost_status::{
EffectiveRouteEnvelope, EffectiveRouteUsage, RouteBillingMode, RuntimeUsageDropRecord,
RuntimeUsageRecord,
};
use crate::models::Role;
use crate::models::{ContentBlock, Message, SystemPrompt, Usage};
use crate::provider_catalog_live::ProviderLivePricingQuote;
use crate::route_budget::{
auto_compact_default_for_route, compaction_threshold_for_route_at_percent, known_route_limits,
@@ -57,6 +55,8 @@ use crate::tools::todo::new_shared_todo_list;
#[cfg(test)]
use codewhale_config::AppMode;
use codewhale_execpolicy::ApprovalMode;
use codewhale_models::Role;
use codewhale_models::{ContentBlock, Message, SystemPrompt, Usage};
use codewhale_protocol::agent_mail::{
AGENT_MAIL_EVENT_DELIVERED, AGENT_MAIL_EVENT_DELIVERING, AGENT_MAIL_EVENT_DELIVERY_FAILED,
AGENT_MAIL_EVENT_QUEUED, AGENT_MAIL_EVENT_READ, AGENT_MAIL_SCHEMA_VERSION, AgentMailAddress,
+4 -4
View File
@@ -426,7 +426,7 @@ mod recovery {
let response = |text: &str, stop: &str| {
let mut events = canned::simple_text_turn(text);
for event in &mut events {
if let crate::models::StreamEvent::MessageDelta { delta, usage } = event {
if let codewhale_models::StreamEvent::MessageDelta { delta, usage } = event {
delta.stop_reason = Some(stop.into());
*usage = Some(Usage {
input_tokens: 11,
@@ -7584,7 +7584,7 @@ async fn monitor_separates_lifecycle_start_from_billing_dispatch_and_child_usage
input_tokens: 10,
output_tokens: 4,
reasoning_replay_tokens: Some(6),
server_tool_use: Some(crate::models::ServerToolUsage {
server_tool_use: Some(codewhale_models::ServerToolUsage {
code_execution_requests: Some(2),
tool_search_requests: Some(3),
}),
@@ -7594,7 +7594,7 @@ async fn monitor_separates_lifecycle_start_from_billing_dispatch_and_child_usage
input_tokens: 10,
output_tokens: 4,
reasoning_replay_tokens: Some(6),
server_tool_use: Some(crate::models::ServerToolUsage {
server_tool_use: Some(codewhale_models::ServerToolUsage {
code_execution_requests: Some(2),
tool_search_requests: Some(3),
}),
@@ -7640,7 +7640,7 @@ async fn monitor_separates_lifecycle_start_from_billing_dispatch_and_child_usage
assert_eq!(persisted_usage.reasoning_replay_tokens, Some(6));
assert_eq!(
persisted_usage.server_tool_use,
Some(crate::models::ServerToolUsage {
Some(codewhale_models::ServerToolUsage {
code_execution_requests: Some(2),
tool_search_requests: Some(3),
})
+2 -2
View File
@@ -161,7 +161,7 @@ fn catalog_model_identifier_is_allowlisted(value: &str) -> bool {
{
return false;
}
crate::model_catalog::resolved_entry(value)
codewhale_models::model_catalog::resolved_entry(value)
.is_some_and(|entry| entry.id == value || entry.provider_model_id.as_deref() == Some(value))
}
@@ -442,7 +442,7 @@ mod tests {
#[test]
fn generic_identifiers_reject_all_slashes_and_catalog_models_require_exact_ids() {
let _catalog_guard = crate::model_catalog::test_catalog_lock();
let _catalog_guard = codewhale_models::model_catalog::test_catalog_lock();
for path in [
"src/lib.rs",
"docs/PREVIEW_REQUEST.md",
+1 -1
View File
@@ -17,10 +17,10 @@ use serde::{Deserialize, Serialize};
use crate::config::ApiProvider;
#[cfg(test)]
use crate::config::{DEEPSEEK_ALIAS_REPLACEMENT, DEEPSEEK_ALIAS_RETIREMENT_UTC};
use crate::models::Usage;
use crate::pricing::{
CostEstimate, TurnCostAudit, audit_turn_cost_for_route_at, token_usage_for_pricing,
};
use codewhale_models::Usage;
/// One turn's normalized token economics.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
+2 -2
View File
@@ -15,14 +15,14 @@
use std::path::{Path, PathBuf};
use crate::models::Role;
use crate::models::{ContentBlock, Message};
use crate::session_manager::{
SavedSession, SessionListFilter, SessionManager, SessionMutator,
create_saved_session_with_id_and_mode,
};
use crate::session_projection::{SessionQuery, SessionSortMode, project_sessions};
use crate::session_resume::{AutoResumeDecision, ResumeRequest, decide_auto_resume};
use codewhale_models::Role;
use codewhale_models::{ContentBlock, Message};
/// Which issue's acceptance list a row comes from.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+14 -14
View File
@@ -10,7 +10,6 @@ use crate::approval_log::{ApprovalReceipt, ApprovalReceiptStore, ApprovalReplay}
use crate::artifacts::ArtifactRecord;
use crate::config::ApiProvider;
use crate::model_routing::AutoRouteReceipt;
use crate::models::{ContentBlock, Message, SystemPrompt};
use crate::project_context::find_git_root;
use crate::session_tree::{SessionEntry, SessionImportContainer, SessionJournal};
use crate::tools::goal::{GoalPauseReason, GoalSnapshot};
@@ -22,6 +21,7 @@ use chrono::{DateTime, Utc};
use codewhale_core::ContextReference;
#[cfg(test)]
use codewhale_core::{ContextReferenceKind, ContextReferenceSource};
use codewhale_models::{ContentBlock, Message, SystemPrompt};
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};
use std::fs::{self, OpenOptions};
@@ -60,7 +60,7 @@ struct LateUsageRecord {
turn_fingerprint: String,
route: crate::cost_status::EffectiveRouteEnvelope,
#[serde(default, skip_serializing_if = "Option::is_none")]
usage: Option<crate::models::Usage>,
usage: Option<codewhale_models::Usage>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -1451,7 +1451,7 @@ impl SessionManager {
turn_id: &str,
source_id: &str,
route: &crate::cost_status::EffectiveRouteEnvelope,
usage: Option<&crate::models::Usage>,
usage: Option<&codewhale_models::Usage>,
) -> io::Result<bool> {
let (path, lock_path) = self.ensure_late_usage_paths(session_id)?;
let lock_file = open_private_lock_file(&lock_path)?;
@@ -3359,10 +3359,10 @@ fn format_age(dt: &DateTime<Utc>) -> String {
mod tests {
use super::*;
use crate::approval_log::ApprovalOutcome;
use crate::models::ContentBlock;
use crate::models::Role;
use crate::tools::plan::StepStatus;
use crate::tui::history::{HistoryCell, ToolCell, history_cells_from_message};
use codewhale_models::ContentBlock;
use codewhale_models::Role;
use std::fs;
use tempfile::tempdir;
@@ -3402,7 +3402,7 @@ mod tests {
Some(crate::config::DEFAULT_DEEPSEEK_BASE_URL),
Utc::now(),
),
usage: crate::models::Usage {
usage: codewhale_models::Usage {
input_tokens: 1,
..Default::default()
},
@@ -4028,10 +4028,10 @@ mod tests {
Some(crate::config::DEFAULT_DEEPSEEK_BASE_URL),
Utc::now(),
);
let usage = crate::models::Usage {
let usage = codewhale_models::Usage {
input_tokens: 17,
output_tokens: 5,
..crate::models::Usage::default()
..codewhale_models::Usage::default()
};
let usage_record = crate::cost_status::RuntimeUsageRecord {
source_id: "translation:old-turn:assistant:1".to_string(),
@@ -4165,9 +4165,9 @@ mod tests {
source_id: format!("late-bounded:{index}"),
usage: crate::cost_status::EffectiveRouteUsage {
route: route.clone(),
usage: crate::models::Usage {
usage: codewhale_models::Usage {
input_tokens: 1,
..crate::models::Usage::default()
..codewhale_models::Usage::default()
},
},
},
@@ -4218,9 +4218,9 @@ mod tests {
source_id: "linked-sidecar-response".to_string(),
usage: crate::cost_status::EffectiveRouteUsage {
route,
usage: crate::models::Usage {
usage: codewhale_models::Usage {
input_tokens: 1,
..crate::models::Usage::default()
..codewhale_models::Usage::default()
},
},
};
@@ -5135,7 +5135,7 @@ mod tests {
);
assert!(loaded.messages.iter().any(|message| {
(message.role == "assistant"
|| message.role == crate::models::INTERRUPTED_ASSISTANT_ROLE)
|| message.role == codewhale_models::INTERRUPTED_ASSISTANT_ROLE)
&& message.content.iter().any(|block| {
matches!(
block,
@@ -5983,7 +5983,7 @@ mod tests {
let mut first = Message {
role: Role::User,
content: vec![ContentBlock::ImageUrl {
image_url: crate::models::ImageUrlContent {
image_url: codewhale_models::ImageUrlContent {
url: "data:image/png;base64,AAAA".to_string(),
},
}],
+4 -4
View File
@@ -22,8 +22,8 @@
use serde::Serialize;
use crate::models::ContentBlock;
use crate::session_manager::SavedSession;
use codewhale_models::ContentBlock;
/// Most entries a peek carries. The dashboard shows a tail, so this is "the
/// last N exchanges", which is what a peek is for.
@@ -299,9 +299,9 @@ fn mask_assignment(token: &str) -> Option<String> {
#[cfg(test)]
mod tests {
use super::*;
use crate::models::Message;
use crate::models::Role;
use crate::session_manager::create_saved_session_with_id_and_mode;
use codewhale_models::Message;
use codewhale_models::Role;
fn text_block(text: &str) -> ContentBlock {
ContentBlock::Text {
@@ -571,7 +571,7 @@ mod tests {
text_block(envelope),
text_block("<image path=\"/tmp/shot.png\">"),
ContentBlock::ImageUrl {
image_url: crate::models::ImageUrlContent {
image_url: codewhale_models::ImageUrlContent {
url: "data:image/png;base64,iVBORw0KGgo=".to_string(),
},
},
Loaded 100 of 151 files, more files were not shown because too many files have changed in this diff. Show more