Skip to main content

sim_codec_chat/providers/
lemonade.rs

1//! Lemonade OpenAI-compatible provider wire codec.
2//!
3//! Lemonade Server accepts OpenAI chat-completions request and response bodies.
4//! This module delegates the wire translation to the OpenAI codec while
5//! preserving the native `lemonade` provider identity on decoded transcripts.
6
7use std::sync::Arc;
8
9use sim_codec::{DecodeLimits, Decoder, DomainCodecLib, Encoder, Input, Output, ReadCx};
10use sim_kernel::{CodecId, Expr, Lib, LibManifest, Linker, LoadCx, Result, Symbol, WriteCx};
11
12use super::openai::OpenAiCodecOptions;
13use super::openai_compat::{
14    decode_request_for_provider, decode_response_for_provider, decode_stream_for_provider,
15    encode_request_for_provider, encode_response_for_codec, encode_response_for_provider,
16};
17
18const LEMONADE_CODEC_ID: CodecId = CodecId(0);
19const PROVIDER: &str = "lemonade";
20
21/// Options for Lemonade OpenAI-compatible request JSON generation.
22pub type LemonadeCodecOptions = OpenAiCodecOptions;
23
24/// Alias for [`LemonadeCodecOptions`] when used for request generation.
25pub type LemonadeRequestOptions = LemonadeCodecOptions;
26
27/// Runtime codec for Lemonade OpenAI-compatible chat-completion JSON.
28pub struct LemonadeCodec;
29
30impl Decoder for LemonadeCodec {
31    fn decode(&self, cx: &mut ReadCx<'_>, input: Input) -> Result<Expr> {
32        decode_request_for_provider(cx.codec, input, PROVIDER, cx.limits)
33    }
34}
35
36impl Encoder for LemonadeCodec {
37    fn encode(&self, cx: &mut WriteCx<'_>, expr: &Expr) -> Result<Output> {
38        encode_response_for_codec(cx.codec, expr).map(Output::Text)
39    }
40}
41
42/// Host-registered lib for `codec:lemonade`.
43pub struct LemonadeCodecLib {
44    symbol: Symbol,
45    codec_id: CodecId,
46}
47
48impl LemonadeCodecLib {
49    /// Creates the lib bound to the given runtime-assigned codec id.
50    pub fn new(id: CodecId) -> Self {
51        Self {
52            symbol: lemonade_codec_symbol(),
53            codec_id: id,
54        }
55    }
56
57    fn domain_lib(&self) -> DomainCodecLib {
58        DomainCodecLib::new(
59            self.symbol.clone(),
60            self.codec_id,
61            Arc::new(LemonadeCodec),
62            Arc::new(LemonadeCodec),
63            Symbol::qualified("codec", "LemonadeTranscript"),
64        )
65    }
66}
67
68impl Lib for LemonadeCodecLib {
69    fn manifest(&self) -> LibManifest {
70        self.domain_lib().manifest()
71    }
72
73    fn load(&self, cx: &mut LoadCx, linker: &mut Linker<'_>) -> Result<()> {
74        self.domain_lib().load(cx, linker)
75    }
76}
77
78/// Returns the codec symbol `codec:lemonade`.
79pub fn lemonade_codec_symbol() -> Symbol {
80    Symbol::qualified("codec", PROVIDER)
81}
82
83/// Decodes a Lemonade OpenAI-compatible request body.
84pub fn decode_lemonade_request(input: Input) -> Result<Expr> {
85    decode_lemonade_request_with_limits(input, DecodeLimits::default())
86}
87
88/// Decodes a Lemonade OpenAI-compatible request body under explicit limits.
89pub fn decode_lemonade_request_with_limits(input: Input, limits: DecodeLimits) -> Result<Expr> {
90    decode_request_for_provider(LEMONADE_CODEC_ID, input, PROVIDER, limits)
91}
92
93/// Encodes a model-request transcript into a Lemonade request body.
94pub fn encode_lemonade_request(expr: &Expr, options: &LemonadeRequestOptions) -> Result<Vec<u8>> {
95    encode_request_for_provider(expr, options)
96}
97
98/// Decodes a Lemonade OpenAI-compatible response body.
99pub fn decode_lemonade_response(
100    runner: Symbol,
101    model: &str,
102    body: &[u8],
103    include_raw: bool,
104) -> Result<Expr> {
105    decode_lemonade_response_with_limits(runner, model, body, include_raw, DecodeLimits::default())
106}
107
108/// Decodes a Lemonade OpenAI-compatible response body under explicit limits.
109pub fn decode_lemonade_response_with_limits(
110    runner: Symbol,
111    model: &str,
112    body: &[u8],
113    include_raw: bool,
114    limits: DecodeLimits,
115) -> Result<Expr> {
116    decode_response_for_provider(
117        LEMONADE_CODEC_ID,
118        runner,
119        model,
120        body,
121        include_raw,
122        PROVIDER,
123        limits,
124    )
125}
126
127/// Decodes Lemonade OpenAI-compatible SSE chunks into a response transcript.
128pub fn decode_lemonade_stream(
129    runner: Symbol,
130    model: &str,
131    body: &[u8],
132    include_raw: bool,
133) -> Result<Expr> {
134    decode_lemonade_stream_with_limits(runner, model, body, include_raw, DecodeLimits::default())
135}
136
137/// Decodes Lemonade OpenAI-compatible SSE chunks under explicit limits.
138pub fn decode_lemonade_stream_with_limits(
139    runner: Symbol,
140    model: &str,
141    body: &[u8],
142    include_raw: bool,
143    limits: DecodeLimits,
144) -> Result<Expr> {
145    decode_stream_for_provider(
146        LEMONADE_CODEC_ID,
147        runner,
148        model,
149        body,
150        include_raw,
151        PROVIDER,
152        limits,
153    )
154}
155
156/// Encodes a model-response transcript into a Lemonade response body.
157pub fn encode_lemonade_response(expr: &Expr) -> Result<Vec<u8>> {
158    encode_response_for_provider(expr)
159}