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::{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)
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_request_for_provider(LEMONADE_CODEC_ID, input, PROVIDER)
86}
87
88/// Encodes a model-request transcript into a Lemonade request body.
89pub fn encode_lemonade_request(expr: &Expr, options: &LemonadeRequestOptions) -> Result<Vec<u8>> {
90    encode_request_for_provider(expr, options)
91}
92
93/// Decodes a Lemonade OpenAI-compatible response body.
94pub fn decode_lemonade_response(
95    runner: Symbol,
96    model: &str,
97    body: &[u8],
98    include_raw: bool,
99) -> Result<Expr> {
100    decode_response_for_provider(runner, model, body, include_raw, PROVIDER)
101}
102
103/// Decodes Lemonade OpenAI-compatible SSE chunks into a response transcript.
104pub fn decode_lemonade_stream(
105    runner: Symbol,
106    model: &str,
107    body: &[u8],
108    include_raw: bool,
109) -> Result<Expr> {
110    decode_stream_for_provider(runner, model, body, include_raw, PROVIDER)
111}
112
113/// Encodes a model-response transcript into a Lemonade response body.
114pub fn encode_lemonade_response(expr: &Expr) -> Result<Vec<u8>> {
115    encode_response_for_provider(expr)
116}