Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
172 commits
Select commit Hold shift + click to select a range
3ec95da
refactor(dgw): give the gateway state the provisioner task store
irvingoujAtDevolution Oct 6, 2026
5580197
feat(dgw): run AI analysis as a provisioner task
irvingoujAtDevolution Oct 7, 2026
be52997
feat(dgw): add endpoints to start and read an AI analysis
irvingoujAtDevolution Oct 6, 2026
9deda22
fix(dgw): store provisioner task jobs before reporting them queued
irvingoujAtDevolution Oct 8, 2026
3593960
fix(dgw): keep the stored job of a provisioner task on restart
irvingoujAtDevolution Oct 8, 2026
c9500e6
docs(dgw): add the provisioner tasks intent
irvingoujAtDevolution Oct 8, 2026
af7f8ba
docs(dgw): point the provisioner tasks intent to the task store crate
irvingoujAtDevolution Oct 8, 2026
b683520
docs(dgw): add the jrec intent
irvingoujAtDevolution Oct 8, 2026
3cac87c
docs(dgw): expand the provisioner tasks intent
irvingoujAtDevolution Oct 8, 2026
c420b44
test(dgw): keep the DVLS 2022.3 scope snapshot frozen
irvingoujAtDevolution Oct 13, 2026
0b5ff11
fix(dgw): keep the AI provider URL out of Debug output
irvingoujAtDevolution Oct 13, 2026
d6e6f24
fix(dgw): bound the AI analysis progress percent in the OpenAPI schema
irvingoujAtDevolution Oct 13, 2026
9989f30
fix(dgw): keep Gateway starting when provisioner tasks cannot be resumed
irvingoujAtDevolution Oct 13, 2026
4081194
style(dgw): separate items and group imports in the AI analysis code
irvingoujAtDevolution Oct 13, 2026
6de83e9
fix(dgw): ignore unknown fields in the AI analysis request
irvingoujAtDevolution Oct 13, 2026
b13cbc4
fix(dgw): start no AI analysis attempt that cannot finish before the …
irvingoujAtDevolution Oct 13, 2026
b52f5f2
refactor(dgw): keep only step, done and total in the AI analysis prog…
irvingoujAtDevolution Oct 13, 2026
6411d8f
fix(dgw): wait for the remux of the session before the AI analysis re…
irvingoujAtDevolution Oct 13, 2026
12f20f0
fix(dgw): tie the AI analysis working files to their Task
irvingoujAtDevolution Oct 13, 2026
623870a
feat(dgw): start a new AI analysis Task of a recording from scratch
irvingoujAtDevolution Oct 13, 2026
5e35da5
style(dgw): format the AI analysis task tests
irvingoujAtDevolution Oct 13, 2026
d3b404b
feat(dgw): write the AI analysis in the language of the request
irvingoujAtDevolution Oct 20, 2026
ccac667
test(dgw): give the fixture of another task its language
irvingoujAtDevolution Oct 20, 2026
acd4ce3
fix(dgw): clear the earlier AI analysis on every attempt of a new Task
irvingoujAtDevolution Oct 20, 2026
ec45a54
docs(dgw): describe the params of an AI analysis Task in the OpenAPI …
irvingoujAtDevolution Oct 20, 2026
59336f3
test(dgw): run the AI analysis endpoint tests against the Gateway binary
irvingoujAtDevolution Oct 20, 2026
186ce36
fix(dgw): name the AI provider schema of the Task params as the reque…
irvingoujAtDevolution Oct 20, 2026
4d20697
fix(dgw): fail an analysis of a deleted recording as missing, not as …
irvingoujAtDevolution Oct 20, 2026
1cfa739
test(dgw): stop Gateway before its config folder goes
irvingoujAtDevolution Oct 20, 2026
bd319d8
style(dgw): use the imported AI provider name in the OpenAPI components
irvingoujAtDevolution Oct 20, 2026
a6623de
test(dgw): check the reason of an analysis of a deleted recording
irvingoujAtDevolution Oct 20, 2026
8baa97b
docs(dgw): describe the maximum output tokens with the range the serv…
irvingoujAtDevolution Oct 20, 2026
7eb6e1c
refactor(dgw): keep the AI access in the secret store
irvingoujAtDevolution Oct 21, 2026
d96a450
docs(dgw): name the SecretStore in the tasks intent
irvingoujAtDevolution Oct 21, 2026
85675cb
docs(dgw): settle the AI analysis intent
irvingoujAtDevolution Oct 22, 2026
0cb0074
feat(dgw): let the expiry of a stored secret move
irvingoujAtDevolution Oct 22, 2026
f8428c3
feat(dgw): run the AI analysis Task as its intent says
irvingoujAtDevolution Oct 22, 2026
4eb0cef
fix(dgw): retry the end of a provisioner Task until it succeeds
irvingoujAtDevolution Oct 22, 2026
101f701
fix(dgw): count an AI request as unreported until it is answered
irvingoujAtDevolution Oct 22, 2026
309988f
fix(dgw): never list a late AI analysis log over a newer one
irvingoujAtDevolution Oct 22, 2026
74270b5
fix(dgw): keep the manifest's temporary file out of jrec downloads
irvingoujAtDevolution Oct 22, 2026
5bde360
fix(dgw): keep the documented AI analysis error kinds in step
irvingoujAtDevolution Oct 22, 2026
4665ec2
docs(dgw): mark the AI analysis key expiry as untested
irvingoujAtDevolution Oct 22, 2026
224fce9
refactor(dgw): name the progress values the AI analysis records
irvingoujAtDevolution Oct 22, 2026
b7f5523
test(dgw): name the AI error mapping test after what it checks
irvingoujAtDevolution Oct 22, 2026
1940c04
fix(dgw): list the log of an earlier success instead of dropping it
irvingoujAtDevolution Oct 22, 2026
c851728
fix(dgw): retry the end when a failed success cannot queue it
irvingoujAtDevolution Oct 22, 2026
fb69a68
fix(dgw): stop the build on an undocumented AI analysis error field
irvingoujAtDevolution Oct 22, 2026
037b77b
refactor(dgw): name the manifest's temporary file after the manifest
irvingoujAtDevolution Oct 22, 2026
c4f1613
docs(dgw): say what keeps a moved secret expiry on time
irvingoujAtDevolution Oct 22, 2026
859c989
fix(dgw): end a Task whose success and failure both went unrecorded
irvingoujAtDevolution Oct 22, 2026
76cabe1
fix(dgw): read only Tasks of the same session for an unlisted log
irvingoujAtDevolution Oct 22, 2026
2b774aa
refactor(dgw): keep one rule for what the end does to an unlisted log
irvingoujAtDevolution Oct 22, 2026
73b84bb
docs(dgw): say why a running Task's deadline moves
irvingoujAtDevolution Oct 27, 2026
e1af190
test(dgw): mark tests kept for the faults they catch
irvingoujAtDevolution Oct 27, 2026
a02ddc0
test(dgw): run the progress test past its first deadline
irvingoujAtDevolution Oct 27, 2026
348aeb1
test(dgw): cite the intent that decides two tests
irvingoujAtDevolution Oct 27, 2026
acac05f
test(dgw): refuse the recordings read scope on a Task read
irvingoujAtDevolution Oct 27, 2026
7e2a925
feat(dotnet-utils): add the AI analysis task scopes
irvingoujAtDevolution Oct 6, 2026
3c7e58b
chore(openapi): regenerate clients for AI analysis
irvingoujAtDevolution Oct 6, 2026
bfe2474
chore(openapi): describe the AI analysis language and prompt versions
irvingoujAtDevolution Oct 19, 2026
5d96923
chore(openapi): say a producer writes the session language
irvingoujAtDevolution Oct 20, 2026
b078279
chore(openapi): add the AI analysis language to the clients
irvingoujAtDevolution Oct 20, 2026
824e953
chore(openapi): describe the params of an AI analysis Task
irvingoujAtDevolution Oct 20, 2026
c1bd086
chore(openapi): redact the API key in ToString and widen the max outp…
irvingoujAtDevolution Oct 20, 2026
ca57dff
test(dotnet-utils): cite the jrec intent for the AI analysis scopes
irvingoujAtDevolution Oct 22, 2026
96115f2
chore(openapi): match the AI analysis Task models to Gateway
irvingoujAtDevolution Oct 27, 2026
6eae99c
refactor(dgw): serve the AI analysis under /jet/tasks/recording-ai-an…
irvingoujAtDevolution Oct 28, 2026
d8779ce
refactor(dgw): mark pending remuxes on disk for the AI analysis
irvingoujAtDevolution Oct 28, 2026
c7a2125
refactor(dgw): count Task attempts on the record only, check scopes i…
irvingoujAtDevolution Oct 28, 2026
d33e3ac
refactor(dgw): give every unfinished Task a new job at startup
irvingoujAtDevolution Oct 28, 2026
0c28981
fix(dgw): name the AI analysis schemas the Task schema refers to
irvingoujAtDevolution Oct 28, 2026
9199edb
fix(dgw): let a forgotten remux mark expire before an AI analysis tim…
irvingoujAtDevolution Oct 28, 2026
e445efa
refactor(dgw): have every provisioner task say whether a restart fail…
irvingoujAtDevolution Oct 28, 2026
6c27973
test(dgw): check the AI analysis across a restart
irvingoujAtDevolution Oct 28, 2026
6f5a7a1
fix(dgw): age a remux mark from the start of its remux
irvingoujAtDevolution Oct 28, 2026
d361310
test(dgw): check provisioner tasks when their end cannot be recorded
irvingoujAtDevolution Oct 28, 2026
5071628
test(dgw): mark what the AI analysis tests check beyond the intent
irvingoujAtDevolution Oct 28, 2026
ef72ee8
fix(dgw): say what a remux mark covers before its job starts
irvingoujAtDevolution Oct 28, 2026
c02d198
refactor(dgw): keep AI analysis failures with their kind, run an atte…
irvingoujAtDevolution Oct 28, 2026
aa673ff
fix(dgw): pass clippy, rustfmt and the retry test of the task routes
irvingoujAtDevolution Oct 28, 2026
9497d0f
refactor(dgw): name a Task's own failures by kind and convert them wi…
irvingoujAtDevolution Oct 28, 2026
535f209
Merge branch 'feat/recording-ai-analysis' into feat/ai-analysis-openapi
irvingoujAtDevolution Oct 28, 2026
0a81468
chore(openapi): move the AI analysis to the Tasks routes and give it …
irvingoujAtDevolution Oct 28, 2026
bc1393d
refactor(dgw): read a recording for the AI analysis without waiting f…
irvingoujAtDevolution Oct 28, 2026
1859efe
refactor(dgw): drop the storage failure of the AI analysis
irvingoujAtDevolution Oct 28, 2026
e343e0d
Merge branch 'feat/recording-ai-analysis' into feat/ai-analysis-openapi
irvingoujAtDevolution Oct 28, 2026
88ffd4c
chore(openapi): drop the storage recoverable failure of the AI analysis
irvingoujAtDevolution Oct 28, 2026
5f2bdf8
refactor(dgw): give the gateway state the provisioner task store
irvingoujAtDevolution Oct 6, 2026
67f134f
feat(dgw): run AI analysis as a provisioner task
irvingoujAtDevolution Oct 7, 2026
c379b23
feat(dgw): add endpoints to start and read an AI analysis
irvingoujAtDevolution Oct 6, 2026
cba330c
fix(dgw): store provisioner task jobs before reporting them queued
irvingoujAtDevolution Oct 8, 2026
0d94000
fix(dgw): keep the stored job of a provisioner task on restart
irvingoujAtDevolution Oct 8, 2026
eb4547c
docs(dgw): add the provisioner tasks intent
irvingoujAtDevolution Oct 8, 2026
b6ef90e
docs(dgw): point the provisioner tasks intent to the task store crate
irvingoujAtDevolution Oct 8, 2026
54b61cb
docs(dgw): add the jrec intent
irvingoujAtDevolution Oct 8, 2026
f78dc41
docs(dgw): expand the provisioner tasks intent
irvingoujAtDevolution Oct 8, 2026
2fa28a3
test(dgw): keep the DVLS 2022.3 scope snapshot frozen
irvingoujAtDevolution Oct 13, 2026
a556dd6
fix(dgw): keep the AI provider URL out of Debug output
irvingoujAtDevolution Oct 13, 2026
ae9b252
fix(dgw): bound the AI analysis progress percent in the OpenAPI schema
irvingoujAtDevolution Oct 13, 2026
d77eaa0
fix(dgw): keep Gateway starting when provisioner tasks cannot be resumed
irvingoujAtDevolution Oct 13, 2026
0055e44
style(dgw): separate items and group imports in the AI analysis code
irvingoujAtDevolution Oct 13, 2026
ba8341d
fix(dgw): ignore unknown fields in the AI analysis request
irvingoujAtDevolution Oct 13, 2026
2ced8c7
fix(dgw): start no AI analysis attempt that cannot finish before the …
irvingoujAtDevolution Oct 13, 2026
e946651
refactor(dgw): keep only step, done and total in the AI analysis prog…
irvingoujAtDevolution Oct 13, 2026
567229c
fix(dgw): wait for the remux of the session before the AI analysis re…
irvingoujAtDevolution Oct 13, 2026
839e3b3
fix(dgw): tie the AI analysis working files to their Task
irvingoujAtDevolution Oct 13, 2026
cc765f4
feat(dgw): start a new AI analysis Task of a recording from scratch
irvingoujAtDevolution Oct 13, 2026
be1cef5
style(dgw): format the AI analysis task tests
irvingoujAtDevolution Oct 13, 2026
626ebcb
feat(dgw): write the AI analysis in the language of the request
irvingoujAtDevolution Oct 20, 2026
50e1b1d
test(dgw): give the fixture of another task its language
irvingoujAtDevolution Oct 20, 2026
68d7619
fix(dgw): clear the earlier AI analysis on every attempt of a new Task
irvingoujAtDevolution Oct 20, 2026
72c1b28
docs(dgw): describe the params of an AI analysis Task in the OpenAPI …
irvingoujAtDevolution Oct 20, 2026
c4150e0
test(dgw): run the AI analysis endpoint tests against the Gateway binary
irvingoujAtDevolution Oct 20, 2026
d7e6ff2
fix(dgw): name the AI provider schema of the Task params as the reque…
irvingoujAtDevolution Oct 20, 2026
2d99d33
fix(dgw): fail an analysis of a deleted recording as missing, not as …
irvingoujAtDevolution Oct 20, 2026
72d1559
test(dgw): stop Gateway before its config folder goes
irvingoujAtDevolution Oct 20, 2026
5e23d92
style(dgw): use the imported AI provider name in the OpenAPI components
irvingoujAtDevolution Oct 20, 2026
8c72c43
test(dgw): check the reason of an analysis of a deleted recording
irvingoujAtDevolution Oct 20, 2026
333c87a
docs(dgw): describe the maximum output tokens with the range the serv…
irvingoujAtDevolution Oct 20, 2026
938b89a
refactor(dgw): keep the AI access in the secret store
irvingoujAtDevolution Oct 21, 2026
a285daa
docs(dgw): name the SecretStore in the tasks intent
irvingoujAtDevolution Oct 21, 2026
a338b62
docs(dgw): settle the AI analysis intent
irvingoujAtDevolution Oct 22, 2026
69991ab
feat(dgw): let the expiry of a stored secret move
irvingoujAtDevolution Oct 22, 2026
330f505
feat(dgw): run the AI analysis Task as its intent says
irvingoujAtDevolution Oct 22, 2026
6b9c9c8
fix(dgw): retry the end of a provisioner Task until it succeeds
irvingoujAtDevolution Oct 22, 2026
08beff0
fix(dgw): count an AI request as unreported until it is answered
irvingoujAtDevolution Oct 22, 2026
3386fab
fix(dgw): never list a late AI analysis log over a newer one
irvingoujAtDevolution Oct 22, 2026
b11c0ff
fix(dgw): keep the manifest's temporary file out of jrec downloads
irvingoujAtDevolution Oct 22, 2026
57e05db
fix(dgw): keep the documented AI analysis error kinds in step
irvingoujAtDevolution Oct 22, 2026
5f31e70
docs(dgw): mark the AI analysis key expiry as untested
irvingoujAtDevolution Oct 22, 2026
64fe1bd
refactor(dgw): name the progress values the AI analysis records
irvingoujAtDevolution Oct 22, 2026
70a5098
test(dgw): name the AI error mapping test after what it checks
irvingoujAtDevolution Oct 22, 2026
3a95145
fix(dgw): list the log of an earlier success instead of dropping it
irvingoujAtDevolution Oct 22, 2026
ea03a4c
fix(dgw): retry the end when a failed success cannot queue it
irvingoujAtDevolution Oct 22, 2026
a2b3751
fix(dgw): stop the build on an undocumented AI analysis error field
irvingoujAtDevolution Oct 22, 2026
4caaadc
refactor(dgw): name the manifest's temporary file after the manifest
irvingoujAtDevolution Oct 22, 2026
b932a97
docs(dgw): say what keeps a moved secret expiry on time
irvingoujAtDevolution Oct 22, 2026
7100e2f
fix(dgw): end a Task whose success and failure both went unrecorded
irvingoujAtDevolution Oct 22, 2026
61f5b57
fix(dgw): read only Tasks of the same session for an unlisted log
irvingoujAtDevolution Oct 22, 2026
840f472
refactor(dgw): keep one rule for what the end does to an unlisted log
irvingoujAtDevolution Oct 22, 2026
c424750
docs(dgw): say why a running Task's deadline moves
irvingoujAtDevolution Oct 27, 2026
de4010d
test(dgw): mark tests kept for the faults they catch
irvingoujAtDevolution Oct 27, 2026
4736580
test(dgw): run the progress test past its first deadline
irvingoujAtDevolution Oct 27, 2026
fbb05df
test(dgw): cite the intent that decides two tests
irvingoujAtDevolution Oct 27, 2026
b5fb2e2
test(dgw): refuse the recordings read scope on a Task read
irvingoujAtDevolution Oct 27, 2026
1060659
refactor(dgw): serve the AI analysis under /jet/tasks/recording-ai-an…
irvingoujAtDevolution Oct 28, 2026
f9e45d2
refactor(dgw): mark pending remuxes on disk for the AI analysis
irvingoujAtDevolution Oct 28, 2026
9963cb0
refactor(dgw): count Task attempts on the record only, check scopes i…
irvingoujAtDevolution Oct 28, 2026
2f1aaa1
refactor(dgw): give every unfinished Task a new job at startup
irvingoujAtDevolution Oct 28, 2026
fb531c4
fix(dgw): name the AI analysis schemas the Task schema refers to
irvingoujAtDevolution Oct 28, 2026
8a2b8cd
fix(dgw): let a forgotten remux mark expire before an AI analysis tim…
irvingoujAtDevolution Oct 28, 2026
453c132
refactor(dgw): have every provisioner task say whether a restart fail…
irvingoujAtDevolution Oct 28, 2026
25d5de3
test(dgw): check the AI analysis across a restart
irvingoujAtDevolution Oct 28, 2026
1b49e4a
fix(dgw): age a remux mark from the start of its remux
irvingoujAtDevolution Oct 28, 2026
48f15f4
test(dgw): check provisioner tasks when their end cannot be recorded
irvingoujAtDevolution Oct 28, 2026
e40467d
test(dgw): mark what the AI analysis tests check beyond the intent
irvingoujAtDevolution Oct 28, 2026
b3295d9
fix(dgw): say what a remux mark covers before its job starts
irvingoujAtDevolution Oct 28, 2026
549b2cd
refactor(dgw): keep AI analysis failures with their kind, run an atte…
irvingoujAtDevolution Oct 28, 2026
0f78134
fix(dgw): pass clippy, rustfmt and the retry test of the task routes
irvingoujAtDevolution Oct 28, 2026
af35820
refactor(dgw): name a Task's own failures by kind and convert them wi…
irvingoujAtDevolution Oct 28, 2026
a14aee1
refactor(dgw): read a recording for the AI analysis without waiting f…
irvingoujAtDevolution Oct 28, 2026
f860456
refactor(dgw): drop the storage failure of the AI analysis
irvingoujAtDevolution Oct 28, 2026
6a57963
fix(dgw): keep the end a Task owes on its record and never rerun a st…
irvingoujAtDevolution Oct 28, 2026
f949771
refactor(dgw): run each Task attempt as a TaskJob hydrated from job d…
irvingoujAtDevolution Oct 29, 2026
62fba8f
refactor(dgw): retry Task attempts inside the job and drop the job token
irvingoujAtDevolution Oct 29, 2026
6d21bff
refactor(dgw): store a Task's job with its record and split the task …
irvingoujAtDevolution Oct 30, 2026
d82fd46
refactor(dgw): store a new Task's job without a delay
irvingoujAtDevolution Oct 30, 2026
1c48e8b
Merge remote-tracking branch 'origin/feat/recording-ai-analysis' into…
irvingoujAtDevolution Oct 30, 2026
5f6bf04
chore: drop the Task runner the merge left behind
irvingoujAtDevolution Oct 30, 2026
8eec5d3
chore(openapi): drop the notQueued failure of the AI analysis
irvingoujAtDevolution Oct 30, 2026
21d599c
test(dotnet-utils): cite the AI analysis intent where it now lives
irvingoujAtDevolution Oct 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 10 additions & 4 deletions crates/devolutions-gateway-ai/examples/analyze_recording.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,10 +246,16 @@ async fn main() -> anyhow::Result<()> {
print_actions(&std::fs::read_to_string(&analysis.log_path)?)?;
println!();

let usage = analysis.usage.map_or_else(
|| "not reported".to_owned(),
|usage| format!("{} in, {} out", usage.input_tokens, usage.output_tokens),
);
let usage = match analysis.usage.unreported_requests {
0 => format!(
"{} in, {} out",
analysis.usage.input_tokens, analysis.usage.output_tokens
),
unreported => format!(
"{} in, {} out, and {unreported} requests not reported",
analysis.usage.input_tokens, analysis.usage.output_tokens
),
};
println!(" {} {}", style(DIM, "model "), analysis.model);
println!(" {} {}", style(DIM, "prompt "), analysis.prompt_version);
println!(" {} {usage}", style(DIM, "tokens "));
Expand Down
61 changes: 5 additions & 56 deletions crates/devolutions-gateway-ai/src/recording_analysis/checkpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,68 +8,26 @@ use anyhow::Context as _;
use camino::{Utf8Path, Utf8PathBuf};
use serde::{Deserialize, Serialize};

use crate::Response;
use crate::session_actions::Action;
use crate::{Response, Usage};

/// Tokens counted by the AI provider, as kept in checkpoints.
///
/// It mirrors [`Usage`], so a change of [`Usage`] never changes the checkpoint format.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct TokenUsage {
input_tokens: u64,
output_tokens: u64,
}

impl From<Usage> for TokenUsage {
fn from(usage: Usage) -> Self {
Self {
input_tokens: usage.input_tokens,
output_tokens: usage.output_tokens,
}
}
}

impl From<TokenUsage> for Usage {
fn from(usage: TokenUsage) -> Self {
Self {
input_tokens: usage.input_tokens,
output_tokens: usage.output_tokens,
}
}
}

pub(super) fn path(workspace: &Utf8Path, index: usize) -> Utf8PathBuf {
workspace.join(format!("chunk-{index:04}.json"))
}

/// Actions found in one chunk, with what the AI provider reported for the requests about it.
#[derive(Debug, Clone, PartialEq)]
/// Actions found in one chunk, with the model that described them.
#[derive(Debug, Clone, Default, PartialEq)]
pub(super) struct DescribedChunk {
pub(super) actions: Vec<Action>,
/// Model that answered, as reported by the provider.
pub(super) model: Option<String>,
/// Tokens of every request about the chunk, answers cut and asked again included; `None` when one was not reported.
pub(super) usage: Option<Usage>,
}

impl Default for DescribedChunk {
/// No action yet, and no token counted yet.
fn default() -> Self {
Self {
actions: Vec::new(),
model: None,
usage: Some(Usage::default()),
}
}
}

impl DescribedChunk {
/// Adds the answer to one request about the chunk.
pub(super) fn add(&mut self, response: Response<Vec<Action>>) {
self.actions.extend(response.output);
self.model = self.model.take().or(response.model);
self.usage = super::add_usage(self.usage, response.usage);
}
}

Expand All @@ -78,8 +36,6 @@ impl DescribedChunk {
struct SavedChunk {
#[serde(default, skip_serializing_if = "Option::is_none")]
model: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
usage: Option<TokenUsage>,
actions: Vec<SavedAction>,
}

Expand All @@ -100,7 +56,6 @@ pub(super) fn write(path: &Utf8Path, chunk: &DescribedChunk) -> anyhow::Result<(

let saved = SavedChunk {
model: chunk.model.clone(),
usage: chunk.usage.map(TokenUsage::from),
actions: chunk
.actions
.iter()
Expand Down Expand Up @@ -141,7 +96,6 @@ pub(super) fn read(path: &Utf8Path) -> anyhow::Result<DescribedChunk> {
Ok(DescribedChunk {
actions,
model: saved.model,
usage: saved.usage.map(Usage::from),
})
}

Expand Down Expand Up @@ -169,10 +123,6 @@ mod tests {
},
],
model: Some("gpt-test-2026-09-30".to_owned()),
usage: Some(Usage {
input_tokens: 10,
output_tokens: 20,
}),
};

write(&path, &chunk).expect("written");
Expand All @@ -183,18 +133,17 @@ mod tests {
}

#[test]
fn unreported_model_and_usage_stay_unknown() {
fn an_unreported_model_stays_unknown() {
// oracle: property round-trip
let dir = tempfile::tempdir().expect("temp dir");
let path = path(Utf8Path::from_path(dir.path()).expect("UTF-8"), 0);
let chunk = DescribedChunk {
actions: Vec::new(),
model: None,
usage: None,
};

write(&path, &chunk).expect("written");

assert_eq!(std::fs::read_to_string(&path).expect("checkpoint"), r#"{"actions":[]}"#);
assert_eq!(read(&path).expect("read"), chunk);
}
}
Loading
Loading