Add Epiphany view lens wrapper
This commit is contained in:
parent
a0f383ef80
commit
e551bbf6c5
8 changed files with 221 additions and 0 deletions
8
vendor/codex/codex-rs/app-server-protocol/schema/json/ClientRequest.json
generated
vendored
8
vendor/codex/codex-rs/app-server-protocol/schema/json/ClientRequest.json
generated
vendored
|
|
@ -4749,6 +4749,14 @@
|
|||
},
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadEpiphanyViewLens": {
|
||||
"enum": [
|
||||
"scene",
|
||||
"jobs",
|
||||
"pressure"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ThreadForkParams": {
|
||||
"description": "There are two ways to fork a thread: 1. By thread_id: load the thread from disk by thread_id and fork it into a new thread. 2. By path: load the thread from disk by path and fork it into a new thread.\n\nIf using path, the thread_id param will be ignored.\n\nPrefer using thread_id whenever possible.",
|
||||
"properties": {
|
||||
|
|
|
|||
|
|
@ -17833,6 +17833,14 @@
|
|||
},
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadEpiphanyViewLens": {
|
||||
"enum": [
|
||||
"scene",
|
||||
"jobs",
|
||||
"pressure"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ThreadForkParams": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "There are two ways to fork a thread: 1. By thread_id: load the thread from disk by thread_id and fork it into a new thread. 2. By path: load the thread from disk by path and fork it into a new thread.\n\nIf using path, the thread_id param will be ignored.\n\nPrefer using thread_id whenever possible.",
|
||||
|
|
|
|||
|
|
@ -15720,6 +15720,14 @@
|
|||
},
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadEpiphanyViewLens": {
|
||||
"enum": [
|
||||
"scene",
|
||||
"jobs",
|
||||
"pressure"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ThreadForkParams": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "There are two ways to fork a thread: 1. By thread_id: load the thread from disk by thread_id and fork it into a new thread. 2. By path: load the thread from disk by path and fork it into a new thread.\n\nIf using path, the thread_id param will be ignored.\n\nPrefer using thread_id whenever possible.",
|
||||
|
|
|
|||
5
vendor/codex/codex-rs/app-server-protocol/schema/typescript/v2/ThreadEpiphanyViewLens.ts
generated
vendored
Normal file
5
vendor/codex/codex-rs/app-server-protocol/schema/typescript/v2/ThreadEpiphanyViewLens.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type ThreadEpiphanyViewLens = "scene" | "jobs" | "pressure";
|
||||
1
vendor/codex/codex-rs/app-server-protocol/schema/typescript/v2/index.ts
generated
vendored
1
vendor/codex/codex-rs/app-server-protocol/schema/typescript/v2/index.ts
generated
vendored
|
|
@ -387,6 +387,7 @@ export type { ThreadEpiphanyStateUpdatedField } from "./ThreadEpiphanyStateUpdat
|
|||
export type { ThreadEpiphanyStateUpdatedNotification } from "./ThreadEpiphanyStateUpdatedNotification";
|
||||
export type { ThreadEpiphanyStateUpdatedSource } from "./ThreadEpiphanyStateUpdatedSource";
|
||||
export type { ThreadEpiphanyUpdatePatch } from "./ThreadEpiphanyUpdatePatch";
|
||||
export type { ThreadEpiphanyViewLens } from "./ThreadEpiphanyViewLens";
|
||||
export type { ThreadForkParams } from "./ThreadForkParams";
|
||||
export type { ThreadForkResponse } from "./ThreadForkResponse";
|
||||
export type { ThreadInjectItemsParams } from "./ThreadInjectItemsParams";
|
||||
|
|
|
|||
|
|
@ -341,6 +341,11 @@ client_request_definitions! {
|
|||
params: v2::ThreadEpiphanySceneParams,
|
||||
response: v2::ThreadEpiphanySceneResponse,
|
||||
},
|
||||
#[experimental("thread/epiphany/view")]
|
||||
ThreadEpiphanyView => "thread/epiphany/view" {
|
||||
params: v2::ThreadEpiphanyViewParams,
|
||||
response: v2::ThreadEpiphanyViewResponse,
|
||||
},
|
||||
#[experimental("thread/epiphany/jobs")]
|
||||
ThreadEpiphanyJobs => "thread/epiphany/jobs" {
|
||||
params: v2::ThreadEpiphanyJobsParams,
|
||||
|
|
@ -1891,6 +1896,68 @@ mod tests {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_thread_epiphany_view_response() -> Result<()> {
|
||||
let request = ClientRequest::ThreadEpiphanyView {
|
||||
request_id: RequestId::Integer(8),
|
||||
params: v2::ThreadEpiphanyViewParams {
|
||||
thread_id: "thr_123".to_string(),
|
||||
lenses: vec![v2::ThreadEpiphanyViewLens::Pressure],
|
||||
},
|
||||
};
|
||||
assert_eq!(request.method(), "thread/epiphany/view");
|
||||
|
||||
let response = ClientResponse::ThreadEpiphanyView {
|
||||
request_id: RequestId::Integer(8),
|
||||
response: v2::ThreadEpiphanyViewResponse {
|
||||
thread_id: "thr_123".to_string(),
|
||||
lenses: vec![v2::ThreadEpiphanyViewLens::Pressure],
|
||||
scene: None,
|
||||
jobs: Vec::new(),
|
||||
pressure: Some(v2::ThreadEpiphanyPressure {
|
||||
status: v2::ThreadEpiphanyPressureStatus::Ready,
|
||||
level: v2::ThreadEpiphanyPressureLevel::Low,
|
||||
basis: v2::ThreadEpiphanyPressureBasis::ModelContextWindow,
|
||||
used_tokens: Some(10),
|
||||
model_context_window: Some(100),
|
||||
model_auto_compact_token_limit: Some(100),
|
||||
remaining_tokens: Some(90),
|
||||
ratio_per_mille: Some(100),
|
||||
should_prepare_compaction: false,
|
||||
note: "Context window usage is below the compaction prep threshold."
|
||||
.to_string(),
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
assert_eq!(response.id(), &RequestId::Integer(8));
|
||||
assert_eq!(response.method(), "thread/epiphany/view");
|
||||
assert_eq!(
|
||||
json!({
|
||||
"method": "thread/epiphany/view",
|
||||
"id": 8,
|
||||
"response": {
|
||||
"threadId": "thr_123",
|
||||
"lenses": ["pressure"],
|
||||
"pressure": {
|
||||
"status": "ready",
|
||||
"level": "low",
|
||||
"basis": "modelContextWindow",
|
||||
"usedTokens": 10,
|
||||
"modelContextWindow": 100,
|
||||
"modelAutoCompactTokenLimit": 100,
|
||||
"remainingTokens": 90,
|
||||
"ratioPerMille": 100,
|
||||
"shouldPrepareCompaction": false,
|
||||
"note": "Context window usage is below the compaction prep threshold."
|
||||
}
|
||||
}
|
||||
}),
|
||||
serde_json::to_value(&response)?,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_thread_epiphany_jobs_response() -> Result<()> {
|
||||
let response = ClientResponse::ThreadEpiphanyJobs {
|
||||
|
|
|
|||
|
|
@ -3940,6 +3940,41 @@ pub struct ThreadEpiphanySceneResponse {
|
|||
pub scene: ThreadEpiphanyScene,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub enum ThreadEpiphanyViewLens {
|
||||
Scene,
|
||||
Jobs,
|
||||
Pressure,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct ThreadEpiphanyViewParams {
|
||||
pub thread_id: String,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub lenses: Vec<ThreadEpiphanyViewLens>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct ThreadEpiphanyViewResponse {
|
||||
pub thread_id: String,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub lenses: Vec<ThreadEpiphanyViewLens>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional = nullable)]
|
||||
pub scene: Option<ThreadEpiphanyScene>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub jobs: Vec<ThreadEpiphanyJob>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional = nullable)]
|
||||
pub pressure: Option<ThreadEpiphanyPressure>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
|
|
|
|||
|
|
@ -266,6 +266,9 @@ use codex_app_server_protocol::ThreadEpiphanyStateUpdatedSource;
|
|||
use codex_app_server_protocol::ThreadEpiphanyUpdateParams;
|
||||
use codex_app_server_protocol::ThreadEpiphanyUpdatePatch;
|
||||
use codex_app_server_protocol::ThreadEpiphanyUpdateResponse;
|
||||
use codex_app_server_protocol::ThreadEpiphanyViewLens;
|
||||
use codex_app_server_protocol::ThreadEpiphanyViewParams;
|
||||
use codex_app_server_protocol::ThreadEpiphanyViewResponse;
|
||||
use codex_app_server_protocol::ThreadForkParams;
|
||||
use codex_app_server_protocol::ThreadForkResponse;
|
||||
use codex_app_server_protocol::ThreadIncrementElicitationParams;
|
||||
|
|
@ -1120,6 +1123,10 @@ impl CodexMessageProcessor {
|
|||
self.thread_epiphany_scene(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
}
|
||||
ClientRequest::ThreadEpiphanyView { request_id, params } => {
|
||||
self.thread_epiphany_view(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
}
|
||||
ClientRequest::ThreadEpiphanyJobs { request_id, params } => {
|
||||
self.thread_epiphany_jobs(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
|
|
@ -4290,6 +4297,88 @@ impl CodexMessageProcessor {
|
|||
self.outgoing.send_response(request_id, response).await;
|
||||
}
|
||||
|
||||
async fn thread_epiphany_view(
|
||||
&self,
|
||||
request_id: ConnectionRequestId,
|
||||
params: ThreadEpiphanyViewParams,
|
||||
) {
|
||||
let ThreadEpiphanyViewParams { thread_id, lenses } = params;
|
||||
let lenses = if lenses.is_empty() {
|
||||
vec![
|
||||
ThreadEpiphanyViewLens::Scene,
|
||||
ThreadEpiphanyViewLens::Jobs,
|
||||
ThreadEpiphanyViewLens::Pressure,
|
||||
]
|
||||
} else {
|
||||
lenses
|
||||
};
|
||||
|
||||
let thread_uuid = match ThreadId::from_string(&thread_id) {
|
||||
Ok(id) => id,
|
||||
Err(err) => {
|
||||
self.send_invalid_request_error(request_id, format!("invalid thread id: {err}"))
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let loaded_thread = self.thread_manager.get_thread(thread_uuid).await.ok();
|
||||
let loaded = loaded_thread.is_some();
|
||||
let thread = match self.read_thread_view(thread_uuid, false).await {
|
||||
Ok(thread) => thread,
|
||||
Err(ThreadReadViewError::InvalidRequest(message)) => {
|
||||
self.send_invalid_request_error(request_id, message).await;
|
||||
return;
|
||||
}
|
||||
Err(ThreadReadViewError::Internal(message)) => {
|
||||
self.send_internal_error(request_id, message).await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let retrieval_override = if lenses.contains(&ThreadEpiphanyViewLens::Jobs)
|
||||
&& thread
|
||||
.epiphany_state
|
||||
.as_ref()
|
||||
.and_then(|state| state.retrieval.as_ref())
|
||||
.is_none()
|
||||
{
|
||||
if let Some(loaded_thread) = loaded_thread.as_ref() {
|
||||
Some(loaded_thread.epiphany_retrieval_state().await)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let token_usage_info = if lenses.contains(&ThreadEpiphanyViewLens::Pressure) {
|
||||
if let Some(loaded_thread) = loaded_thread.as_ref() {
|
||||
loaded_thread.token_usage_info().await
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let response = ThreadEpiphanyViewResponse {
|
||||
thread_id,
|
||||
scene: lenses
|
||||
.contains(&ThreadEpiphanyViewLens::Scene)
|
||||
.then(|| map_epiphany_scene(thread.epiphany_state.as_ref(), loaded)),
|
||||
jobs: if lenses.contains(&ThreadEpiphanyViewLens::Jobs) {
|
||||
map_epiphany_jobs(thread.epiphany_state.as_ref(), retrieval_override.as_ref())
|
||||
} else {
|
||||
Vec::new()
|
||||
},
|
||||
pressure: lenses
|
||||
.contains(&ThreadEpiphanyViewLens::Pressure)
|
||||
.then(|| map_epiphany_pressure(token_usage_info.as_ref())),
|
||||
lenses,
|
||||
};
|
||||
self.outgoing.send_response(request_id, response).await;
|
||||
}
|
||||
|
||||
async fn thread_epiphany_jobs(
|
||||
&self,
|
||||
request_id: ConnectionRequestId,
|
||||
|
|
|
|||
Reference in a new issue