lean_ctx_protocol/
engine_context_plan.rs1use serde::{Deserialize, Serialize};
6
7use crate::{ContextPlanProjectionV1, SemanticVersion, TaskId, ValidationError};
8
9pub const MAX_ENGINE_CONTEXT_PLAN_REQUEST_BYTES: usize = 64 * 1024;
11pub const MAX_ENGINE_CONTEXT_PLAN_QUERY_BYTES: usize = 16 * 1024;
12pub const MAX_ENGINE_CONTEXT_PLAN_TOKENS: u32 = 1_048_576;
13pub const MAX_ENGINE_CONTEXT_PLAN_CANDIDATES: u16 = 256;
14
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
18#[serde(deny_unknown_fields)]
19pub struct EngineContextPlanRequestV1 {
20 pub schema_version: u32,
21 pub transport_version: u32,
22 pub engine_interface_version: SemanticVersion,
23 pub task_id: TaskId,
24 pub query: String,
25 pub budget_tokens: u32,
26 pub max_candidates: u16,
27}
28
29impl EngineContextPlanRequestV1 {
30 pub fn validate_payload(&self) -> Result<(), ValidationError> {
32 if self.query.trim().is_empty()
33 || self.query.len() > MAX_ENGINE_CONTEXT_PLAN_QUERY_BYTES
34 || self.query.contains('\0')
35 || !(1..=MAX_ENGINE_CONTEXT_PLAN_TOKENS).contains(&self.budget_tokens)
36 || !(1..=MAX_ENGINE_CONTEXT_PLAN_CANDIDATES).contains(&self.max_candidates)
37 {
38 return Err(ValidationError::new("invalid Engine context-plan bounds"));
39 }
40 Ok(())
41 }
42}
43
44#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
46#[serde(deny_unknown_fields)]
47pub struct EngineContextPlanResponseV1 {
48 pub schema_version: u32,
49 pub transport_version: u32,
50 pub engine_interface_version: SemanticVersion,
51 pub plan: ContextPlanProjectionV1,
52}