Skip to main content

vtcode_llm/open_responses/
mod.rs

1#![expect(
2    unused_results,
3    reason = "Open Responses bridge code intentionally discards map replacement, stream-drain, and emitter results after updating state."
4)]
5
6//! Open Responses specification conformance layer.
7//!
8//! This module implements the [Open Responses](https://www.openresponses.org/) specification
9//! for vendor-neutral LLM interfaces. It provides:
10//!
11//! - Unified item types with state machine semantics
12//! - Semantic streaming events (not raw token deltas)
13//! - Response objects with standardized structure
14//! - Error handling with structured error types
15//! - Extension points for VT Code-specific item types
16//!
17//! The implementation bridges VT Code's internal event system (`ThreadEvent`)
18//! to Open Responses-compliant structures while maintaining backwards compatibility.
19
20mod bridge;
21mod content;
22mod error;
23mod events;
24mod integration;
25mod items;
26mod request;
27mod response;
28mod status;
29mod usage;
30
31pub use bridge::{DualEventEmitter, ResponseBuilder};
32pub use content::{ContentPart, ImageDetail, InputFileContent, InputImageContent};
33pub use error::{OpenResponseError, OpenResponseErrorCode, OpenResponseErrorType};
34pub use events::{ResponseStreamEvent, SequencedEvent, StreamEventEmitter, VecStreamEmitter};
35pub use integration::{OpenResponsesCallback, OpenResponsesIntegration, OpenResponsesProvider, ToOpenResponse};
36pub use items::{
37    CustomItem, FunctionCallItem, FunctionCallOutputItem, MessageItem, MessageRole, OutputItem, OutputItemId,
38    ReasoningItem,
39};
40pub use request::{Request, SpecificToolChoice, ToolChoice, ToolChoiceMode};
41pub use response::{
42    IncompleteDetails, IncompleteReason, Response, ResponseId, ResponseStatus, generate_item_id, generate_response_id,
43};
44pub use status::ItemStatus;
45pub use usage::{InputTokensDetails, OpenUsage, OutputTokensDetails};
46
47/// VT Code extension prefix for custom item types and events.
48pub const VTCODE_EXTENSION_PREFIX: &str = "vtcode";
49
50/// Validates that a custom type follows the Open Responses extension naming convention.
51/// Custom types must be prefixed with an implementor slug (e.g., `vtcode:file_change`).
52pub fn is_valid_extension_type(type_name: &str) -> bool {
53    if let Some((prefix, name)) = type_name.split_once(':') {
54        !prefix.is_empty()
55            && !name.is_empty()
56            && prefix.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
57            && name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
58    } else {
59        false
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn test_valid_extension_types() {
69        assert!(is_valid_extension_type("vtcode:file_change"));
70        assert!(is_valid_extension_type("acme:search_result"));
71        assert!(is_valid_extension_type("openai:web_search_call"));
72    }
73
74    #[test]
75    fn test_invalid_extension_types() {
76        assert!(!is_valid_extension_type("file_change"));
77        assert!(!is_valid_extension_type(":file_change"));
78        assert!(!is_valid_extension_type("vtcode:"));
79        assert!(!is_valid_extension_type("vt-code:file_change"));
80        assert!(!is_valid_extension_type(""));
81    }
82}