diff --git a/Cargo.lock b/Cargo.lock index ae665c04..c6d104bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -661,8 +661,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-link 0.2.0", ] @@ -1978,6 +1980,7 @@ dependencies = [ "base64 0.22.1", "big_serde_json", "bytes", + "chrono", "coverage-report", "env_logger", "ipnet", @@ -3658,6 +3661,7 @@ version = "11.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ef1b7a6d914a34127ed8e1fa927eb7088903787bcded4fa3eef8f85ee1568be" dependencies = [ + "chrono", "thiserror 2.0.16", "ts-rs-macros", ] diff --git a/bindings/typescript/src/generated/Agent.ts b/bindings/typescript/src/generated/Agent.ts new file mode 100644 index 00000000..d272a6b1 --- /dev/null +++ b/bindings/typescript/src/generated/Agent.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { UserContent } from "./UserContent"; + +export type Agent = { name?: string, version?: string, metadata: Record, instructions?: UserContent, }; diff --git a/bindings/typescript/src/generated/AgentResponse.ts b/bindings/typescript/src/generated/AgentResponse.ts new file mode 100644 index 00000000..a9bb4310 --- /dev/null +++ b/bindings/typescript/src/generated/AgentResponse.ts @@ -0,0 +1,5 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AssistantContent } from "./AssistantContent"; +import type { UniversalUsage } from "./UniversalUsage"; + +export type AgentResponse = { response?: AssistantContent, usage?: UniversalUsage, start_time?: string, end_time?: string, }; diff --git a/bindings/typescript/src/generated/Compaction.ts b/bindings/typescript/src/generated/Compaction.ts new file mode 100644 index 00000000..38b4dc27 --- /dev/null +++ b/bindings/typescript/src/generated/Compaction.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type Compaction = { id: string, replaced_message_count?: number, }; diff --git a/bindings/typescript/src/generated/EvidenceField.ts b/bindings/typescript/src/generated/EvidenceField.ts new file mode 100644 index 00000000..75aecf97 --- /dev/null +++ b/bindings/typescript/src/generated/EvidenceField.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type EvidenceField = "input" | "output" | "error"; diff --git a/bindings/typescript/src/generated/EvidencePart.ts b/bindings/typescript/src/generated/EvidencePart.ts new file mode 100644 index 00000000..9043e5fd --- /dev/null +++ b/bindings/typescript/src/generated/EvidencePart.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type EvidencePart = "reasoning" | "arguments"; diff --git a/bindings/typescript/src/generated/Finding.ts b/bindings/typescript/src/generated/Finding.ts new file mode 100644 index 00000000..ea304f5c --- /dev/null +++ b/bindings/typescript/src/generated/Finding.ts @@ -0,0 +1,5 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { FindingEvidence } from "./FindingEvidence"; +import type { FindingSeverity } from "./FindingSeverity"; + +export type Finding = { title?: string, severity?: FindingSeverity, kind?: string, hypothesis?: string, root_cause?: string, next_steps?: Array, evidence?: Array, pattern_id?: string, }; diff --git a/bindings/typescript/src/generated/FindingEvidence.ts b/bindings/typescript/src/generated/FindingEvidence.ts new file mode 100644 index 00000000..562fd775 --- /dev/null +++ b/bindings/typescript/src/generated/FindingEvidence.ts @@ -0,0 +1,5 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { EvidenceField } from "./EvidenceField"; +import type { EvidencePart } from "./EvidencePart"; + +export type FindingEvidence = { step_id: string, field?: EvidenceField, part?: EvidencePart, explanation?: string, quote?: string, highlight_terms?: Array, leading_text?: string, trailing_text?: string, }; diff --git a/bindings/typescript/src/generated/FindingSeverity.ts b/bindings/typescript/src/generated/FindingSeverity.ts new file mode 100644 index 00000000..cbef2c70 --- /dev/null +++ b/bindings/typescript/src/generated/FindingSeverity.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type FindingSeverity = "critical" | "high" | "medium" | "low"; diff --git a/bindings/typescript/src/generated/InputTokenDetails.ts b/bindings/typescript/src/generated/InputTokenDetails.ts new file mode 100644 index 00000000..f9b99b87 --- /dev/null +++ b/bindings/typescript/src/generated/InputTokenDetails.ts @@ -0,0 +1,24 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ModalityTokenCount } from "./ModalityTokenCount"; +import type { TokenBreakdown } from "./TokenBreakdown"; + +/** + * Detailed subsets of inclusive prompt/input usage. + */ +export type InputTokenDetails = { +/** + * User/request content tokens by modality. Provider-generated tool prompts are separate. + */ +content_by_modality?: Array, +/** + * Cache-read tokens, which are included in `UniversalUsage::prompt_tokens`. + */ +cached?: TokenBreakdown, +/** + * Cache-write tokens, which are included in `UniversalUsage::prompt_tokens` when reported. + */ +cache_creation?: TokenBreakdown, +/** + * Provider-generated tool prompt tokens included in `UniversalUsage::prompt_tokens`. + */ +tool_prompt?: TokenBreakdown, }; diff --git a/bindings/typescript/src/generated/LLMAnalysis.ts b/bindings/typescript/src/generated/LLMAnalysis.ts new file mode 100644 index 00000000..d20a1d14 --- /dev/null +++ b/bindings/typescript/src/generated/LLMAnalysis.ts @@ -0,0 +1,6 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Message } from "./Message"; +import type { UniversalParams } from "./UniversalParams"; +import type { UniversalUsage } from "./UniversalUsage"; + +export type LLMAnalysis = { work?: Array, model?: string, params?: UniversalParams, usage?: UniversalUsage, }; diff --git a/bindings/typescript/src/generated/ModalityTokenCount.ts b/bindings/typescript/src/generated/ModalityTokenCount.ts new file mode 100644 index 00000000..d98680fa --- /dev/null +++ b/bindings/typescript/src/generated/ModalityTokenCount.ts @@ -0,0 +1,7 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { TokenModality } from "./TokenModality"; + +/** + * Token count for one modality. + */ +export type ModalityTokenCount = { modality?: TokenModality, token_count?: number, }; diff --git a/bindings/typescript/src/generated/OutputTokenDetails.ts b/bindings/typescript/src/generated/OutputTokenDetails.ts new file mode 100644 index 00000000..8a3cf2cf --- /dev/null +++ b/bindings/typescript/src/generated/OutputTokenDetails.ts @@ -0,0 +1,16 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ModalityTokenCount } from "./ModalityTokenCount"; +import type { TokenBreakdown } from "./TokenBreakdown"; + +/** + * Detailed subsets of inclusive completion/output usage. + */ +export type OutputTokenDetails = { +/** + * Returned candidate content tokens by modality. Reasoning tokens are separate. + */ +content_by_modality?: Array, +/** + * Reasoning/thinking tokens included in `UniversalUsage::completion_tokens`. + */ +reasoning?: TokenBreakdown, }; diff --git a/bindings/typescript/src/generated/Scope.ts b/bindings/typescript/src/generated/Scope.ts new file mode 100644 index 00000000..8b2b636a --- /dev/null +++ b/bindings/typescript/src/generated/Scope.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type Scope = { "type": "span", id: string, } | { "type": "trace", trace_id: string, } | { "type": "thread", key: string, value: string, }; diff --git a/bindings/typescript/src/generated/Section.ts b/bindings/typescript/src/generated/Section.ts new file mode 100644 index 00000000..cfa34cef --- /dev/null +++ b/bindings/typescript/src/generated/Section.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type Section = { name: string, description: string, step_ids: Array, }; diff --git a/bindings/typescript/src/generated/TokenBreakdown.ts b/bindings/typescript/src/generated/TokenBreakdown.ts new file mode 100644 index 00000000..03195344 --- /dev/null +++ b/bindings/typescript/src/generated/TokenBreakdown.ts @@ -0,0 +1,7 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ModalityTokenCount } from "./ModalityTokenCount"; + +/** + * A token subset with an optional modality breakdown. + */ +export type TokenBreakdown = { total_tokens?: number, by_modality?: Array, }; diff --git a/bindings/typescript/src/generated/TokenModality.ts b/bindings/typescript/src/generated/TokenModality.ts new file mode 100644 index 00000000..56ee6c4e --- /dev/null +++ b/bindings/typescript/src/generated/TokenModality.ts @@ -0,0 +1,6 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * A provider-independent token modality. + */ +export type TokenModality = "unspecified" | "text" | "image" | "audio" | "video" | "document"; diff --git a/bindings/typescript/src/generated/ToolResult.ts b/bindings/typescript/src/generated/ToolResult.ts new file mode 100644 index 00000000..c4df1579 --- /dev/null +++ b/bindings/typescript/src/generated/ToolResult.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ToolContentPart } from "./ToolContentPart"; + +export type ToolResult = { input?: unknown, content?: Array, }; diff --git a/bindings/typescript/src/generated/Trajectory.ts b/bindings/typescript/src/generated/Trajectory.ts new file mode 100644 index 00000000..87d64d83 --- /dev/null +++ b/bindings/typescript/src/generated/Trajectory.ts @@ -0,0 +1,8 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Agent } from "./Agent"; +import type { Finding } from "./Finding"; +import type { Scope } from "./Scope"; +import type { Section } from "./Section"; +import type { Turn } from "./Turn"; + +export type Trajectory = { scope: Array, agent: Agent, sections?: Array
, findings?: Array, turns: Array, metadata: Record, version?: string, }; diff --git a/bindings/typescript/src/generated/Turn.ts b/bindings/typescript/src/generated/Turn.ts new file mode 100644 index 00000000..a8a319e2 --- /dev/null +++ b/bindings/typescript/src/generated/Turn.ts @@ -0,0 +1,8 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AgentResponse } from "./AgentResponse"; +import type { Compaction } from "./Compaction"; +import type { Message } from "./Message"; +import type { UniversalParams } from "./UniversalParams"; +import type { WorkStep } from "./WorkStep"; + +export type Turn = { request_id: string, response_id?: string, request?: Array, response?: AgentResponse, work: Array, model?: string, params?: UniversalParams, start_time: string, end_time?: string, interrupted?: boolean, compaction?: Compaction, }; diff --git a/bindings/typescript/src/generated/UniversalUsage.ts b/bindings/typescript/src/generated/UniversalUsage.ts new file mode 100644 index 00000000..54e44ca2 --- /dev/null +++ b/bindings/typescript/src/generated/UniversalUsage.ts @@ -0,0 +1,60 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { InputTokenDetails } from "./InputTokenDetails"; +import type { OutputTokenDetails } from "./OutputTokenDetails"; + +/** + * Token usage statistics. + */ +export type UniversalUsage = { +/** + * Tokens in the prompt/input, including provider-generated tool prompts when reported. + */ +prompt_tokens?: number, +/** + * Tokens in the completion/output + */ +completion_tokens?: number, +/** + * Total tokens. Preserves a provider-reported value or falls back to prompt plus completion. + */ +total_tokens?: number, +/** + * Cached tokens in the prompt (from prompt caching) + */ +prompt_cached_tokens?: number, +/** + * Tokens written to cache during this request + */ +prompt_cache_creation_tokens?: number, +/** + * Tokens written to the 5-minute-TTL cache (Anthropic split cache writes) + */ +prompt_cache_creation_5m_tokens?: number, +/** + * Tokens written to the 1-hour-TTL cache (Anthropic split cache writes) + */ +prompt_cache_creation_1h_tokens?: number, +/** + * True when `prompt_tokens` excludes the cache read/creation buckets. + * Anthropic-style usage reports `input_tokens` exclusive of cache + * tokens, while OpenAI-style usage reports `prompt_tokens` inclusive of + * them. Consumers that want an OpenAI-style inclusive prompt total must + * add the cache buckets back when this is set; see + * [`UniversalUsage::inclusive_prompt_tokens`]. Consumers that want an + * Anthropic-style exclusive input count must subtract the cache buckets + * when this is not set; see [`UniversalUsage::exclusive_prompt_tokens`]. + */ +prompt_tokens_exclude_cache?: boolean, +/** + * Reasoning/thinking tokens used in the completion. + * `Some(n)` only when `n > 0`; otherwise `None`. + */ +completion_reasoning_tokens?: number, +/** + * Detailed prompt/input token subsets and modality breakdowns. + */ +input_details?: InputTokenDetails, +/** + * Detailed completion/output token subsets and modality breakdowns. + */ +output_details?: OutputTokenDetails, }; diff --git a/bindings/typescript/src/generated/Work.ts b/bindings/typescript/src/generated/Work.ts new file mode 100644 index 00000000..885b31e6 --- /dev/null +++ b/bindings/typescript/src/generated/Work.ts @@ -0,0 +1,6 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AgentResponse } from "./AgentResponse"; +import type { LLMAnalysis } from "./LLMAnalysis"; +import type { ToolResult } from "./ToolResult"; + +export type Work = { "type": "agent_response" } & AgentResponse | { "type": "tool_result" } & ToolResult | { "type": "llm_analysis" } & LLMAnalysis; diff --git a/bindings/typescript/src/generated/WorkStep.ts b/bindings/typescript/src/generated/WorkStep.ts new file mode 100644 index 00000000..1d968acc --- /dev/null +++ b/bindings/typescript/src/generated/WorkStep.ts @@ -0,0 +1,5 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Trajectory } from "./Trajectory"; +import type { Work } from "./Work"; + +export type WorkStep = { id: string, span_type: string, name?: string, error?: unknown, start_time: string, end_time?: string, work: Work, sub_agent?: Trajectory, }; diff --git a/bindings/typescript/src/types.ts b/bindings/typescript/src/types.ts index 2e5f8f8c..5b4be0ac 100644 --- a/bindings/typescript/src/types.ts +++ b/bindings/typescript/src/types.ts @@ -42,3 +42,8 @@ export type { TokenBudget } from "./generated/TokenBudget"; export type { ChatCompletionRequestMessage } from "./generated/openai/ChatCompletionRequestMessage"; export type { InputItem } from "./generated/openai/InputItem"; export type { InputMessage } from "./generated/anthropic/InputMessage"; +export type { Trajectory } from "./generated/Trajectory"; +export type { Turn } from "./generated/Turn"; +export type { WorkStep } from "./generated/WorkStep"; +export type { AgentResponse } from "./generated/AgentResponse"; +export type { UniversalUsage } from "./generated/UniversalUsage"; diff --git a/crates/lingua/Cargo.toml b/crates/lingua/Cargo.toml index 0032bd6c..b2ea3ac0 100644 --- a/crates/lingua/Cargo.toml +++ b/crates/lingua/Cargo.toml @@ -34,7 +34,8 @@ serde_json.workspace = true bytes.workspace = true yaml_serde.workspace = true serde_with.workspace = true -ts-rs.workspace = true +chrono.workspace = true +ts-rs = { workspace = true, features = ["chrono-impl"] } thiserror.workspace = true url.workspace = true urlencoding.workspace = true diff --git a/crates/lingua/src/universal/mod.rs b/crates/lingua/src/universal/mod.rs index 3a866011..fec68ced 100644 --- a/crates/lingua/src/universal/mod.rs +++ b/crates/lingua/src/universal/mod.rs @@ -19,6 +19,7 @@ pub mod response_format; pub mod stream; pub mod tool_choice; pub mod tools; +pub mod trajectory; pub mod transform; // Re-export main types for convenience diff --git a/crates/lingua/src/universal/request.rs b/crates/lingua/src/universal/request.rs index cd942d81..b9548581 100644 --- a/crates/lingua/src/universal/request.rs +++ b/crates/lingua/src/universal/request.rs @@ -91,7 +91,7 @@ pub enum TokenBudget { /// /// Uses canonical names - adapters handle mapping to provider-specific names. /// Provider-specific fields without canonical mappings are stored in `extras`. -#[derive(Debug, Clone, Default, Serialize, TS)] +#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] #[ts(export)] pub struct UniversalParams { // === Sampling parameters === @@ -509,7 +509,7 @@ impl AsRef for SummaryMode { /// - OpenAI Chat: `"auto"` | `"none"` | `"required"` | `{ type: "function", function: { name } }` /// - OpenAI Responses: `"auto"` | `{ type: "function", name }` /// - Anthropic: `{ type: "auto" | "any" | "none" | "tool", name?, disable_parallel_tool_use? }` -#[derive(Debug, Clone, Default, Serialize, TS)] +#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] #[ts(export)] pub struct ToolChoiceConfig { /// Selection mode - the semantic intent of the tool choice @@ -520,7 +520,7 @@ pub struct ToolChoiceConfig { } /// Tool selection mode (portable across providers). -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, TS)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)] #[ts(export)] pub enum ToolChoiceMode { /// Provider decides whether to use tools @@ -595,7 +595,7 @@ impl AsRef for ToolChoiceMode { /// - OpenAI Responses: nested under `text.format` /// - Google: `response_mime_type` + `response_schema` /// - Anthropic: `{ type: "json_schema", schema, name?, strict?, description? }` -#[derive(Debug, Clone, Default, Serialize, TS)] +#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] #[ts(export)] pub struct ResponseFormatConfig { /// Output format type @@ -615,7 +615,7 @@ impl ResponseFormatConfig { } /// Response format type (portable across providers). -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, TS)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)] #[ts(export)] pub enum ResponseFormatType { /// Plain text output (default) @@ -666,7 +666,7 @@ impl AsRef for ResponseFormatType { } /// JSON schema configuration for structured output. -#[derive(Debug, Clone, Serialize, TS)] +#[derive(Debug, Clone, Serialize, Deserialize, TS)] #[ts(export)] pub struct JsonSchemaConfig { /// Schema name (required by OpenAI) diff --git a/crates/lingua/src/universal/response.rs b/crates/lingua/src/universal/response.rs index 085ceb7e..0c01ccb5 100644 --- a/crates/lingua/src/universal/response.rs +++ b/crates/lingua/src/universal/response.rs @@ -11,6 +11,7 @@ use crate::universal::defaults::PLACEHOLDER_ID; use crate::universal::message::{AssistantContent, AssistantContentPart, Message}; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; +use ts_rs::TS; /// Universal response envelope for LLM API responses. /// @@ -79,7 +80,7 @@ impl ParsableResponseInfo { } /// A provider-independent token modality. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, TS)] #[serde(rename_all = "snake_case")] pub enum TokenModality { Unspecified, @@ -91,69 +92,86 @@ pub enum TokenModality { } /// Token count for one modality. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, TS)] pub struct ModalityTokenCount { #[serde(default, skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub modality: Option, #[serde(default, skip_serializing_if = "Option::is_none")] + #[ts(optional, type = "number")] pub token_count: Option, } /// A token subset with an optional modality breakdown. #[skip_serializing_none] -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, TS)] pub struct TokenBreakdown { + #[ts(optional, type = "number")] pub total_tokens: Option, + #[ts(optional)] pub by_modality: Option>, } /// Detailed subsets of inclusive prompt/input usage. #[skip_serializing_none] -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, TS)] pub struct InputTokenDetails { /// User/request content tokens by modality. Provider-generated tool prompts are separate. + #[ts(optional)] pub content_by_modality: Option>, /// Cache-read tokens, which are included in `UniversalUsage::prompt_tokens`. + #[ts(optional)] pub cached: Option, /// Cache-write tokens, which are included in `UniversalUsage::prompt_tokens` when reported. + #[ts(optional)] pub cache_creation: Option, /// Provider-generated tool prompt tokens included in `UniversalUsage::prompt_tokens`. + #[ts(optional)] pub tool_prompt: Option, } /// Detailed subsets of inclusive completion/output usage. #[skip_serializing_none] -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, TS)] pub struct OutputTokenDetails { /// Returned candidate content tokens by modality. Reasoning tokens are separate. + #[ts(optional)] pub content_by_modality: Option>, /// Reasoning/thinking tokens included in `UniversalUsage::completion_tokens`. + #[ts(optional)] pub reasoning: Option, } /// Token usage statistics. #[skip_serializing_none] -#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] pub struct UniversalUsage { /// Tokens in the prompt/input, including provider-generated tool prompts when reported. + #[ts(optional, type = "number")] pub prompt_tokens: Option, /// Tokens in the completion/output + #[ts(optional, type = "number")] pub completion_tokens: Option, /// Total tokens. Preserves a provider-reported value or falls back to prompt plus completion. + #[ts(optional, type = "number")] pub total_tokens: Option, /// Cached tokens in the prompt (from prompt caching) + #[ts(optional, type = "number")] pub prompt_cached_tokens: Option, /// Tokens written to cache during this request + #[ts(optional, type = "number")] pub prompt_cache_creation_tokens: Option, /// Tokens written to the 5-minute-TTL cache (Anthropic split cache writes) + #[ts(optional, type = "number")] pub prompt_cache_creation_5m_tokens: Option, /// Tokens written to the 1-hour-TTL cache (Anthropic split cache writes) + #[ts(optional, type = "number")] pub prompt_cache_creation_1h_tokens: Option, /// True when `prompt_tokens` excludes the cache read/creation buckets. @@ -165,16 +183,20 @@ pub struct UniversalUsage { /// Anthropic-style exclusive input count must subtract the cache buckets /// when this is not set; see [`UniversalUsage::exclusive_prompt_tokens`]. #[serde(default, skip_serializing_if = "std::ops::Not::not")] + #[ts(as = "Option", optional)] pub prompt_tokens_exclude_cache: bool, /// Reasoning/thinking tokens used in the completion. /// `Some(n)` only when `n > 0`; otherwise `None`. + #[ts(optional, type = "number")] pub completion_reasoning_tokens: Option, /// Detailed prompt/input token subsets and modality breakdowns. + #[ts(optional)] pub input_details: Option, /// Detailed completion/output token subsets and modality breakdowns. + #[ts(optional)] pub output_details: Option, } diff --git a/crates/lingua/src/universal/trajectory.rs b/crates/lingua/src/universal/trajectory.rs new file mode 100644 index 00000000..b9da5171 --- /dev/null +++ b/crates/lingua/src/universal/trajectory.rs @@ -0,0 +1,222 @@ +use crate::{ + universal::{AssistantContent, ToolContent, UserContent}, + Message, UniversalParams, UniversalUsage, +}; +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use serde_json::{Map, Value}; +use serde_with::skip_serializing_none; +use std::vec::Vec; +use ts_rs::TS; + +#[skip_serializing_none] +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +#[ts(export)] +pub struct Trajectory { + pub scope: Vec, + pub agent: Agent, + #[ts(optional)] + pub sections: Option>, + #[ts(optional)] + pub findings: Option>, + pub turns: Vec, + #[ts(type = "Record")] + pub metadata: Map, + #[ts(optional)] + pub version: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum Scope { + Span { id: String }, + Trace { trace_id: String }, + Thread { key: String, value: String }, +} + +#[skip_serializing_none] +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +pub struct Agent { + #[ts(optional)] + pub name: Option, + #[ts(optional)] + pub version: Option, + #[ts(type = "Record")] + pub metadata: Map, + #[ts(optional)] + pub instructions: Option, +} + +#[skip_serializing_none] +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +pub struct Section { + pub name: String, + pub description: String, + pub step_ids: Vec, +} + +#[skip_serializing_none] +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +pub struct Finding { + #[ts(optional)] + pub title: Option, + #[ts(optional)] + pub severity: Option, + #[ts(optional)] + pub kind: Option, + #[ts(optional)] + pub hypothesis: Option, + #[ts(optional)] + pub root_cause: Option, + #[ts(optional)] + pub next_steps: Option>, + #[ts(optional)] + pub evidence: Option>, + #[ts(optional)] + pub pattern_id: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +#[serde(rename_all = "snake_case")] +pub enum FindingSeverity { + Critical, + High, + Medium, + Low, +} + +#[skip_serializing_none] +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +pub struct FindingEvidence { + pub step_id: String, + #[ts(optional)] + pub field: Option, + #[ts(optional)] + pub part: Option, + #[ts(optional)] + pub explanation: Option, + #[ts(optional)] + pub quote: Option, + #[ts(optional)] + pub highlight_terms: Option>, + #[ts(optional)] + pub leading_text: Option, + #[ts(optional)] + pub trailing_text: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +#[serde(rename_all = "snake_case")] +pub enum EvidenceField { + Input, + Output, + Error, +} + +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +#[serde(rename_all = "snake_case")] +pub enum EvidencePart { + Reasoning, + Arguments, +} + +#[skip_serializing_none] +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +pub struct Turn { + // Each of these is the id of the corresponding span + pub request_id: String, + #[ts(optional)] + pub response_id: Option, + + // Step definition + #[ts(optional)] + pub request: Option>, + #[ts(optional)] + pub response: Option, + pub work: Vec, + + #[ts(optional)] + pub model: Option, + #[ts(optional)] + pub params: Option, + + pub start_time: DateTime, + #[ts(optional)] + pub end_time: Option>, + #[ts(optional)] + pub interrupted: Option, + #[ts(optional)] + pub compaction: Option, +} + +#[skip_serializing_none] +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +pub struct Compaction { + pub id: String, + #[ts(optional)] + pub replaced_message_count: Option, +} + +#[skip_serializing_none] +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +pub struct AgentResponse { + #[ts(optional)] + pub response: Option, + + #[ts(optional)] + pub usage: Option, + #[ts(optional)] + pub start_time: Option>, + #[ts(optional)] + pub end_time: Option>, +} + +#[skip_serializing_none] +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +pub struct ToolResult { + #[ts(optional, type = "unknown")] + pub input: Option, + #[ts(optional)] + pub content: Option, +} + +#[skip_serializing_none] +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +pub struct LLMAnalysis { + #[ts(optional)] + pub work: Option>, + #[ts(optional)] + pub model: Option, + #[ts(optional)] + pub params: Option, + #[ts(optional)] + pub usage: Option, +} + +#[skip_serializing_none] +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +pub struct WorkStep { + pub id: String, // span's id + pub span_type: String, // span type + #[ts(optional)] + pub name: Option, + #[ts(optional, type = "unknown")] + pub error: Option, + + pub start_time: DateTime, + #[ts(optional)] + pub end_time: Option>, + + pub work: Work, + #[ts(optional)] + pub sub_agent: Option>, +} + +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum Work { + AgentResponse(Box), + ToolResult(Box), + #[serde(rename = "llm_analysis")] + LLMAnalysis(Box), +}