ort_openrouter_cli/output/
from_json.rs

1//! ort: Open Router CLI
2//! https://github.com/grahamking/ort
3//!
4//! MIT License
5//! Copyright (c) 2025 Graham King
6
7use core::str::FromStr;
8
9extern crate alloc;
10use alloc::borrow::{Cow, ToOwned};
11use alloc::format;
12use alloc::string::String;
13use alloc::vec;
14use alloc::vec::Vec;
15
16use crate::common::config;
17use crate::{
18    ChatCompletionsResponse, Choice, LastData, Message, Priority, PromptOpts, ReasoningConfig,
19    ReasoningEffort, Role, Usage,
20};
21
22impl ChatCompletionsResponse {
23    pub fn from_json(json: &str) -> Result<Self, Cow<'static, str>> {
24        let mut p = Parser::new(json);
25        p.skip_ws();
26        p.expect(b'{')?;
27
28        let mut provider = None;
29        let mut model = None;
30        let mut choices = vec![];
31        let mut usage = None;
32
33        loop {
34            p.skip_ws();
35            if p.try_consume(b'}') {
36                break;
37            }
38
39            let key = p
40                .parse_simple_str()
41                .map_err(|err| format!("ChatCompletionsResponse parsing key: {err}"))?;
42            p.skip_ws();
43            p.expect(b':')?;
44            p.skip_ws();
45
46            match key {
47                "provider" => {
48                    if provider.is_some() {
49                        return Err("duplicate field: provider".into());
50                    }
51                    provider = Some(p.parse_string()?);
52                }
53                "model" => {
54                    if model.is_some() {
55                        return Err("duplicate field: model".into());
56                    }
57                    model = Some(p.parse_string()?);
58                }
59                "choices" => {
60                    if !choices.is_empty() {
61                        return Err("duplicate field: choices".into());
62                    }
63                    if !p.try_consume(b'[') {
64                        return Err("keys: Expected array".into());
65                    }
66                    loop {
67                        let j = p.value_slice()?;
68                        let choice = Choice::from_json(j)?;
69                        choices.push(choice);
70                        p.skip_ws();
71                        if p.try_consume(b',') {
72                            continue;
73                        }
74                        p.skip_ws();
75                        if p.try_consume(b']') {
76                            break;
77                        }
78                    }
79                }
80                "usage" => {
81                    let j = p.value_slice()?;
82                    usage = Some(Usage::from_json(j)?);
83                }
84                _ => {
85                    p.skip_value()?;
86                }
87            }
88
89            p.skip_ws();
90            if p.try_consume(b',') {
91                continue;
92            }
93            p.skip_ws();
94            if p.try_consume(b'}') {
95                break;
96            }
97        }
98
99        Ok(ChatCompletionsResponse {
100            provider,
101            model,
102            choices,
103            usage,
104        })
105    }
106}
107
108impl Choice {
109    pub fn from_json(json: &str) -> Result<Self, String> {
110        let mut p = Parser::new(json);
111        p.skip_ws();
112        p.expect(b'{')?;
113
114        let mut delta = None;
115
116        'top: loop {
117            p.skip_ws();
118            if p.try_consume(b'}') {
119                break;
120            }
121
122            let key = p
123                .parse_simple_str()
124                .map_err(|err| format!("Choice::from_json parsing key: {err}"))?;
125            p.skip_ws();
126            p.expect(b':')?;
127            p.skip_ws();
128
129            match key {
130                "delta" => {
131                    let j = p.value_slice()?;
132                    delta = Some(Message::from_json(j)?);
133                    break 'top;
134                }
135                _ => {
136                    p.skip_value()?;
137                }
138            }
139
140            p.skip_ws();
141            if p.try_consume(b',') {
142                continue;
143            }
144            p.skip_ws();
145            if p.try_consume(b'}') {
146                break;
147            }
148        }
149
150        Ok(Choice {
151            delta: delta.expect("Missing delta in message"),
152        })
153    }
154}
155
156impl Usage {
157    pub fn from_json(json: &str) -> Result<Self, String> {
158        let mut p = Parser::new(json);
159        p.skip_ws();
160        p.expect(b'{')?;
161
162        // Currently we only extract cost
163        let mut cost = 0.0;
164
165        'top: loop {
166            p.skip_ws();
167            if p.try_consume(b'}') {
168                break;
169            }
170
171            let key = p
172                .parse_simple_str()
173                .map_err(|err| format!("Usage parsing key: {err}"))?;
174            p.skip_ws();
175            p.expect(b':')?;
176            p.skip_ws();
177
178            match key {
179                "cost" => {
180                    cost = p.parse_f32()?;
181                    // As we only care about cost, we are done as soon as we have it
182                    break 'top;
183                }
184                _ => {
185                    p.skip_value()?;
186                }
187            }
188
189            p.skip_ws();
190            if p.try_consume(b',') {
191                continue;
192            }
193            p.skip_ws();
194            if p.try_consume(b'}') {
195                break;
196            }
197        }
198
199        Ok(Usage { cost })
200    }
201}
202
203impl LastData {
204    pub fn from_json(json: &str) -> Result<Self, Cow<'static, str>> {
205        if json.is_empty() {
206            return Err(
207                "Cannot continue, last-<$TMUX_PANE>.json file is empty. Usually that mains previous run failed.".into(),
208            );
209        }
210        let mut p = Parser::new(json);
211        p.skip_ws();
212        p.expect(b'{')?;
213
214        let mut opts = None;
215        let mut messages = vec![];
216
217        loop {
218            p.skip_ws();
219            if p.try_consume(b'}') {
220                break;
221            }
222
223            let key = p
224                .parse_simple_str()
225                .map_err(|err| format!("LastData parsing key: {err}"))?;
226            p.skip_ws();
227            p.expect(b':')?;
228            p.skip_ws();
229
230            match key {
231                "opts" => {
232                    if opts.is_some() {
233                        return Err("duplicate field: opts".into());
234                    }
235                    let j = p.value_slice()?;
236                    opts = Some(PromptOpts::from_json(j)?);
237                }
238                "messages" => {
239                    if !messages.is_empty() {
240                        return Err("duplicate field: messages".into());
241                    }
242                    if !p.try_consume(b'[') {
243                        return Err("messages: Expected array".into());
244                    }
245                    loop {
246                        let j = p.value_slice()?;
247                        let msg = Message::from_json(j)?;
248                        messages.push(msg);
249                        p.skip_ws();
250                        if p.try_consume(b',') {
251                            continue;
252                        }
253                        p.skip_ws();
254                        if p.try_consume(b']') {
255                            break;
256                        }
257                    }
258                }
259                _ => return Err("unknown field".into()),
260            }
261
262            p.skip_ws();
263            if p.try_consume(b',') {
264                continue;
265            }
266            p.skip_ws();
267            if p.try_consume(b'}') {
268                break;
269            }
270        }
271
272        Ok(LastData {
273            opts: opts.expect("Missing prompt opts"),
274            messages,
275        })
276    }
277}
278
279impl Message {
280    pub fn from_json(json: &str) -> Result<Self, Cow<'static, str>> {
281        let mut p = Parser::new(json);
282        p.skip_ws();
283        p.expect(b'{')?;
284
285        let mut role = None;
286        let mut content = None;
287        let mut reasoning = None;
288
289        loop {
290            p.skip_ws();
291            if p.try_consume(b'}') {
292                break;
293            }
294
295            let key = p
296                .parse_simple_str()
297                .map_err(|err| format!("Message parsing key: {err}"))?;
298            p.skip_ws();
299            p.expect(b':')?;
300            p.skip_ws();
301
302            match key {
303                "role" => {
304                    if role.is_some() {
305                        return Err("duplicate field: role".into());
306                    }
307                    let r = p.parse_simple_str()?;
308                    role = Some(Role::from_str(r)?);
309                }
310                "content" => {
311                    if content.is_some() {
312                        return Err("duplicate field: content".into());
313                    }
314                    if p.peek_is_null() {
315                        p.parse_null()?;
316                        content = None;
317                    } else {
318                        content = Some(p.parse_string()?);
319                    }
320                }
321                "reasoning" => {
322                    if reasoning.is_some() {
323                        return Err("duplicate field: reasoning".into());
324                    }
325                    if p.peek_is_null() {
326                        p.parse_null()?;
327                        reasoning = None
328                    } else {
329                        reasoning = Some(p.parse_string()?);
330                    }
331                }
332                _ => {
333                    p.skip_value()?;
334                }
335            }
336
337            p.skip_ws();
338            if p.try_consume(b',') {
339                continue;
340            }
341            p.skip_ws();
342            if p.try_consume(b'}') {
343                break;
344            }
345        }
346
347        Ok(Message::new(
348            role.expect("Missing Role"),
349            content,
350            reasoning,
351        ))
352    }
353}
354
355impl ReasoningConfig {
356    pub fn from_json(json: &str) -> Result<ReasoningConfig, Cow<'static, str>> {
357        let mut p = Parser::new(json);
358        p.skip_ws();
359        p.expect(b'{')?;
360
361        let mut enabled: Option<bool> = None;
362        let mut effort: Option<ReasoningEffort> = None;
363        let mut tokens: Option<u32> = None;
364
365        loop {
366            p.skip_ws();
367            if p.try_consume(b'}') {
368                break;
369            }
370
371            // Key
372            let key = p
373                .parse_simple_str()
374                .map_err(|err| format!("ReasoningConfig parsing key: {err}"))?;
375            p.skip_ws();
376            p.expect(b':')?;
377            p.skip_ws();
378
379            // Value by key
380            match key {
381                "enabled" => {
382                    if enabled.is_some() {
383                        return Err("duplicate field: enabled".into());
384                    }
385                    if p.peek_is_null() {
386                        p.parse_null()?;
387                        enabled = None;
388                    } else {
389                        enabled = Some(p.parse_bool()?);
390                    }
391                }
392                "effort" => {
393                    if effort.is_some() {
394                        return Err("duplicate field: effort".into());
395                    }
396                    if p.peek_is_null() {
397                        p.parse_null()?;
398                        effort = None;
399                    } else {
400                        let v = p
401                            .parse_simple_str()
402                            .map_err(|err| format!("Parsing effort: {err}"))?;
403                        let e = if v.eq_ignore_ascii_case("none") {
404                            ReasoningEffort::None
405                        } else if v.eq_ignore_ascii_case("low") {
406                            ReasoningEffort::Low
407                        } else if v.eq_ignore_ascii_case("medium") {
408                            ReasoningEffort::Medium
409                        } else if v.eq_ignore_ascii_case("high") {
410                            ReasoningEffort::High
411                        } else if v.eq_ignore_ascii_case("xhigh") {
412                            ReasoningEffort::XHigh
413                        } else {
414                            return Err("invalid effort".into());
415                        };
416                        effort = Some(e);
417                    }
418                }
419                "tokens" => {
420                    if tokens.is_some() {
421                        return Err("duplicate field: tokens".into());
422                    }
423                    if p.peek_is_null() {
424                        p.parse_null()?;
425                        tokens = None;
426                    } else {
427                        tokens = Some(p.parse_u32()?);
428                    }
429                }
430                _ => return Err("unknown field".into()),
431            }
432
433            p.skip_ws();
434            if p.try_consume(b',') {
435                continue;
436            }
437
438            p.skip_ws();
439            if p.try_consume(b'}') {
440                break;
441            }
442
443            // If neither comma nor closing brace, it's malformed.
444            if !p.eof() {
445                return Err("expected ',' or '}'".into());
446            } else {
447                return Err("unexpected end of input".into());
448            }
449        }
450
451        p.skip_ws();
452        if !p.eof() {
453            return Err("trailing characters after JSON object".into());
454        }
455
456        let enabled = enabled.ok_or("missing required field: enabled")?;
457
458        Ok(ReasoningConfig {
459            enabled,
460            effort,
461            tokens,
462        })
463    }
464}
465
466impl PromptOpts {
467    pub fn from_json(input: &str) -> Result<Self, Cow<'static, str>> {
468        let mut p = Parser::new(input);
469
470        p.skip_ws();
471        p.expect(b'{')?;
472
473        let mut prompt: Option<String> = None;
474        let mut model: Option<String> = None;
475        let mut provider: Option<String> = None;
476        let mut system: Option<String> = None;
477        let mut priority: Option<Priority> = None;
478        let mut reasoning: Option<ReasoningConfig> = None;
479        let mut show_reasoning: Option<bool> = None;
480        let mut quiet: Option<bool> = None;
481        let mut merge_config = true;
482
483        p.skip_ws();
484        if p.try_consume(b'}') {
485            return Ok(PromptOpts {
486                prompt,
487                models: vec![],
488                provider,
489                system,
490                priority,
491                reasoning,
492                show_reasoning,
493                quiet,
494                merge_config,
495            });
496        }
497
498        loop {
499            p.skip_ws();
500            let key = p.parse_simple_str()?;
501            p.skip_ws();
502            p.expect(b':')?;
503            p.skip_ws();
504
505            match key {
506                "prompt" => {
507                    prompt = p.parse_opt_string()?;
508                }
509                "model" => {
510                    model = p.parse_opt_string()?;
511                }
512                "provider" => {
513                    provider = p.parse_opt_string()?;
514                }
515                "system" => {
516                    system = p.parse_opt_string()?;
517                }
518                "priority" => {
519                    if p.peek_is_null() {
520                        p.parse_null()?;
521                        priority = None;
522                    } else {
523                        let s = p.parse_simple_str()?;
524                        priority = Some(Priority::from_str(s).map_err(|_| "invalid priority")?);
525                    }
526                }
527                "reasoning" => {
528                    if p.peek_is_null() {
529                        p.parse_null()?;
530                        reasoning = None;
531                    } else {
532                        // Grab the exact object slice and delegate to ReasoningConfig::from_json
533                        let slice = p.value_slice()?; // must be an object
534                        let cfg = ReasoningConfig::from_json(slice).map_err(|e| {
535                            format!("parser::PromptOpts::from_json {e}, invalid reasoning")
536                        })?;
537                        reasoning = Some(cfg);
538                    }
539                }
540                "show_reasoning" => {
541                    if p.peek_is_null() {
542                        p.parse_null()?;
543                        show_reasoning = None;
544                    } else {
545                        show_reasoning = Some(p.parse_bool()?);
546                    }
547                }
548                "quiet" => {
549                    if p.peek_is_null() {
550                        p.parse_null()?;
551                        quiet = None;
552                    } else {
553                        quiet = Some(p.parse_bool()?);
554                    }
555                }
556                "merge_config" => {
557                    if p.peek_is_null() {
558                        p.parse_null()?;
559                        merge_config = true;
560                    } else {
561                        merge_config = p.parse_bool()?;
562                    }
563                }
564                _ => {
565                    // Unknown field: skip its value
566                    p.skip_value()?;
567                }
568            }
569
570            p.skip_ws();
571            if p.try_consume(b',') {
572                continue;
573            } else {
574                p.expect(b'}')?;
575                break;
576            }
577        }
578
579        Ok(PromptOpts {
580            prompt,
581            models: model.map(|m| vec![m]).unwrap_or_default(),
582            provider,
583            system,
584            priority,
585            reasoning,
586            show_reasoning,
587            quiet,
588            merge_config,
589        })
590    }
591}
592
593impl config::ConfigFile {
594    pub fn from_json(json: &str) -> Result<Self, Cow<'static, str>> {
595        let mut p = Parser::new(json);
596        p.skip_ws();
597        p.expect(b'{')?;
598
599        let mut settings: Option<config::Settings> = None;
600        let mut keys: Vec<config::ApiKey> = vec![];
601        let mut prompt_opts: Option<PromptOpts> = None;
602
603        loop {
604            p.skip_ws();
605            if p.try_consume(b'}') {
606                break;
607            }
608
609            let key = p
610                .parse_simple_str()
611                .map_err(|err| format!("ConfigFile parsing key: {err}"))?;
612            p.skip_ws();
613            p.expect(b':')?;
614            p.skip_ws();
615
616            match key {
617                "settings" => {
618                    if settings.is_some() {
619                        return Err("duplicate field: settings".into());
620                    }
621                    let settings_json = p.value_slice()?;
622                    settings = Some(config::Settings::from_json(settings_json)?);
623                }
624                "keys" => {
625                    if !keys.is_empty() {
626                        return Err("duplicate field: keys".into());
627                    }
628                    if !p.try_consume(b'[') {
629                        return Err("keys: Expected array".into());
630                    }
631                    loop {
632                        let j = p.value_slice()?;
633                        let api_key = config::ApiKey::from_json(j)?;
634                        keys.push(api_key);
635                        p.skip_ws();
636                        if p.try_consume(b',') {
637                            continue;
638                        }
639                        p.skip_ws();
640                        if p.try_consume(b']') {
641                            break;
642                        }
643                    }
644                }
645                "prompt_opts" => {
646                    if prompt_opts.is_some() {
647                        return Err("duplicate field: prompt_opts".into());
648                    }
649                    let opts_json = p.value_slice()?;
650                    prompt_opts = Some(PromptOpts::from_json(opts_json)?);
651                }
652                _ => return Err("unknown field".into()),
653            }
654            p.skip_ws();
655            if p.try_consume(b',') {
656                continue;
657            }
658            p.skip_ws();
659            if p.try_consume(b'}') {
660                break;
661            }
662        }
663
664        Ok(config::ConfigFile {
665            settings,
666            keys,
667            prompt_opts,
668        })
669    }
670}
671
672impl config::Settings {
673    pub fn from_json(json: &str) -> Result<Self, Cow<'static, str>> {
674        let mut p = Parser::new(json);
675        p.skip_ws();
676        p.expect(b'{')?;
677
678        let mut save_to_file = None;
679        let mut dns = vec![];
680
681        loop {
682            p.skip_ws();
683            if p.try_consume(b'}') {
684                break;
685            }
686
687            let key = p
688                .parse_simple_str()
689                .map_err(|err| format!("Settings parsing key: {err}"))?;
690            p.skip_ws();
691            p.expect(b':')?;
692            p.skip_ws();
693
694            match key {
695                "save_to_file" => {
696                    if save_to_file.is_some() {
697                        return Err("duplicate field: save_to_file".into());
698                    }
699                    if p.peek_is_null() {
700                        p.parse_null()?;
701                        save_to_file = None;
702                    } else {
703                        save_to_file = Some(p.parse_bool()?);
704                    }
705                }
706                "dns" => {
707                    if !dns.is_empty() {
708                        return Err("duplicate field: dns".into());
709                    }
710                    if !p.try_consume(b'[') {
711                        return Err("dns: Expected array".into());
712                    }
713                    loop {
714                        let addr = p.parse_string()?;
715                        dns.push(addr);
716                        p.skip_ws();
717                        if p.try_consume(b',') {
718                            continue;
719                        }
720                        p.skip_ws();
721                        if p.try_consume(b']') {
722                            break;
723                        }
724                    }
725                }
726                _ => return Err("unknown field".into()),
727            }
728
729            p.skip_ws();
730            if p.try_consume(b',') {
731                continue;
732            }
733            p.skip_ws();
734            if p.try_consume(b'}') {
735                break;
736            }
737
738            // If neither comma nor closing brace, it's malformed.
739            if !p.eof() {
740                return Err("expected ',' or '}'".into());
741            } else {
742                return Err("unexpected end of input".into());
743            }
744        }
745
746        let default = config::Settings::default();
747        Ok(config::Settings {
748            save_to_file: save_to_file.unwrap_or(default.save_to_file),
749            dns,
750        })
751    }
752}
753
754impl config::ApiKey {
755    pub fn from_json(json: &str) -> Result<Self, Cow<'static, str>> {
756        let mut p = Parser::new(json);
757        p.skip_ws();
758        p.expect(b'{')?;
759
760        let mut name = None;
761        let mut value = None;
762
763        loop {
764            p.skip_ws();
765            if p.try_consume(b'}') {
766                break;
767            }
768
769            let key = p
770                .parse_simple_str()
771                .map_err(|err| format!("ApiKey parsing key: {err}"))?;
772            p.skip_ws();
773            p.expect(b':')?;
774            p.skip_ws();
775
776            match key {
777                "name" => {
778                    if name.is_some() {
779                        return Err("duplicate field: name".into());
780                    }
781                    name = Some(
782                        p.parse_string()
783                            .map_err(|err| format!("Parsing name: {err}"))?,
784                    );
785                }
786                "value" => {
787                    if value.is_some() {
788                        return Err("duplicate field: value".into());
789                    }
790                    value = Some(
791                        p.parse_string()
792                            .map_err(|err| format!("Parsing name: {err}"))?,
793                    );
794                }
795                _ => return Err("unknown field".into()),
796            }
797            p.skip_ws();
798            if p.try_consume(b',') {
799                continue;
800            } else {
801                p.expect(b'}')?;
802                break;
803            }
804        }
805
806        Ok(config::ApiKey::new(
807            name.expect("Missing ApiKey name"),
808            value.expect("Missing ApiKey value"),
809        ))
810    }
811}
812
813// --------------------------------------------
814
815// Minimal, fast JSON scanner tailored for our needs.
816struct Parser<'a> {
817    s: &'a str,
818    b: &'a [u8],
819    i: usize,
820}
821
822impl<'a> Parser<'a> {
823    fn new(s: &'a str) -> Self {
824        Self {
825            s,
826            b: s.as_bytes(),
827            i: 0,
828        }
829    }
830
831    fn eof(&self) -> bool {
832        self.i >= self.b.len()
833    }
834
835    fn peek(&self) -> Option<u8> {
836        if self.eof() {
837            None
838        } else {
839            Some(self.b[self.i])
840        }
841    }
842
843    fn try_consume(&mut self, ch: u8) -> bool {
844        if self.peek() == Some(ch) {
845            self.i += 1;
846            true
847        } else {
848            false
849        }
850    }
851
852    fn expect(&mut self, ch: u8) -> Result<(), &'static str> {
853        if self.try_consume(ch) {
854            Ok(())
855        } else {
856            Err("expected character")
857        }
858    }
859
860    fn skip_ws(&mut self) {
861        while let Some(c) = self.peek() {
862            match c {
863                b' ' | b'\n' | b'\r' | b'\t' => self.i += 1,
864                _ => break,
865            }
866        }
867    }
868
869    fn starts_with_bytes(&self, pat: &[u8]) -> bool {
870        let end = self.i + pat.len();
871        end <= self.b.len() && &self.b[self.i..end] == pat
872    }
873
874    fn parse_null(&mut self) -> Result<(), &'static str> {
875        if self.starts_with_bytes(b"null") {
876            self.i += 4;
877            Ok(())
878        } else {
879            Err("expected null")
880        }
881    }
882
883    fn peek_is_null(&self) -> bool {
884        self.starts_with_bytes(b"null")
885    }
886
887    fn parse_bool(&mut self) -> Result<bool, String> {
888        self.skip_ws();
889        if self.starts_with_bytes(b"true") {
890            self.i += 4;
891            Ok(true)
892        } else if self.starts_with_bytes(b"false") {
893            self.i += 5;
894            Ok(false)
895        } else {
896            Err(format!(
897                "Expected boolean, got '{}'",
898                String::from_utf8_lossy(&self.b[self.i..])
899            ))
900        }
901    }
902
903    fn parse_u32(&mut self) -> Result<u32, &'static str> {
904        self.skip_ws();
905        if self.eof() {
906            return Err("expected number");
907        }
908        if self.peek() == Some(b'-') {
909            return Err("negative not allowed");
910        }
911        let mut val: u32 = 0;
912        let mut read_any = false;
913        let len = self.b.len();
914        while self.i < len {
915            let c = self.b[self.i];
916            if c.is_ascii_digit() {
917                read_any = true;
918                let digit = (c - b'0') as u32;
919                // Overflow-safe accumulation
920                if val > (u32::MAX - digit) / 10 {
921                    return Err("u32 overflow");
922                }
923                val = val * 10 + digit;
924                self.i += 1;
925            } else {
926                break;
927            }
928        }
929        if !read_any {
930            return Err("expected integer");
931        }
932        Ok(val)
933    }
934
935    fn parse_f32(&mut self) -> Result<f32, &'static str> {
936        self.skip_ws();
937        if self.eof() {
938            return Err("expected number");
939        }
940
941        let len = self.b.len();
942
943        // Sign
944        let mut neg = false;
945        if let Some(c) = self.peek() {
946            if c == b'-' {
947                neg = true;
948                self.i += 1;
949            } else if c == b'+' {
950                self.i += 1;
951            }
952        }
953
954        // Mantissa accumulation (up to 9 significant digits)
955        let mut mant: u32 = 0;
956        let mut mant_digits: i32 = 0;
957        let mut ints: i32 = 0;
958
959        // Integer part
960        while self.i < len {
961            let c = self.b[self.i];
962            if c.is_ascii_digit() {
963                if mant_digits < 9 {
964                    mant = mant.saturating_mul(10).wrapping_add((c - b'0') as u32);
965                    mant_digits += 1;
966                }
967                self.i += 1;
968                ints += 1;
969            } else {
970                break;
971            }
972        }
973
974        // Fractional part
975        let mut frac_any = false;
976        if self.peek() == Some(b'.') {
977            self.i += 1;
978            let start_frac = self.i;
979            while self.i < len {
980                let c = self.b[self.i];
981                if c.is_ascii_digit() {
982                    if mant_digits < 9 {
983                        mant = mant.saturating_mul(10).wrapping_add((c - b'0') as u32);
984                        mant_digits += 1;
985                    }
986                    self.i += 1;
987                } else {
988                    break;
989                }
990            }
991            frac_any = self.i > start_frac;
992        }
993
994        if ints == 0 && !frac_any {
995            return Err("expected number");
996        }
997
998        // Exponent part
999        let mut exp_part: i32 = 0;
1000        if let Some(ech) = self.peek()
1001            && (ech == b'e' || ech == b'E')
1002        {
1003            self.i += 1;
1004            let mut eneg = false;
1005            if let Some(signch) = self.peek() {
1006                if signch == b'-' {
1007                    eneg = true;
1008                    self.i += 1;
1009                } else if signch == b'+' {
1010                    self.i += 1;
1011                }
1012            }
1013            if self.eof() || !self.b[self.i].is_ascii_digit() {
1014                return Err("expected exponent");
1015            }
1016            let mut eacc: i32 = 0;
1017            while self.i < len {
1018                let c = self.b[self.i];
1019                if c.is_ascii_digit() {
1020                    let d = (c - b'0') as i32;
1021                    if eacc < 1_000_000_000 / 10 {
1022                        eacc = eacc * 10 + d;
1023                    } else {
1024                        eacc = 1_000_000_000; // clamp large exponents
1025                    }
1026                    self.i += 1;
1027                } else {
1028                    break;
1029                }
1030            }
1031            exp_part = if eneg { -eacc } else { eacc };
1032        }
1033
1034        // Effective base-10 exponent relative to the mantissa we built
1035        let exp10 = ints - mant_digits + exp_part;
1036
1037        // Scale using f64 to avoid premature underflow; cast to f32 at the end
1038        let mut val = mant as f64;
1039
1040        const POW10_POS: [f64; 39] = [
1041            1.0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
1042            1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
1043            1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38,
1044        ];
1045        const POW10_NEG: [f64; 46] = [
1046            1.0, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8, 1e-9, 1e-10, 1e-11, 1e-12, 1e-13,
1047            1e-14, 1e-15, 1e-16, 1e-17, 1e-18, 1e-19, 1e-20, 1e-21, 1e-22, 1e-23, 1e-24, 1e-25,
1048            1e-26, 1e-27, 1e-28, 1e-29, 1e-30, 1e-31, 1e-32, 1e-33, 1e-34, 1e-35, 1e-36, 1e-37,
1049            1e-38, 1e-39, 1e-40, 1e-41, 1e-42, 1e-43, 1e-44, 1e-45,
1050        ];
1051
1052        if exp10 > 0 {
1053            let mut e = exp10;
1054            while e > 0 {
1055                let chunk = if e > 38 { 38 } else { e } as usize;
1056                val *= POW10_POS[chunk];
1057                if !val.is_finite() {
1058                    return Err("f32 overflow");
1059                }
1060                e -= chunk as i32;
1061            }
1062        } else if exp10 < 0 {
1063            let mut e = -exp10;
1064            while e > 0 {
1065                let chunk = if e > 45 { 45 } else { e } as usize;
1066                val *= POW10_NEG[chunk];
1067                if val == 0.0 {
1068                    break;
1069                }
1070                e -= chunk as i32;
1071            }
1072        }
1073
1074        let mut out = val as f32;
1075        if !out.is_finite() {
1076            return Err("f32 overflow");
1077        }
1078        if neg {
1079            out = -out;
1080        }
1081        Ok(out)
1082    }
1083
1084    fn parse_simple_str(&mut self) -> Result<&'a str, &'static str> {
1085        self.skip_ws();
1086        if self.peek() != Some(b'"') {
1087            return Err("expected string");
1088        }
1089        self.i += 1;
1090        let start = self.i;
1091        let len = self.b.len();
1092        while self.i < len {
1093            let c = self.b[self.i];
1094            if c == b'\\' {
1095                // For maximum speed and simplicity, we reject escapes.
1096                return Err("string escapes are not supported");
1097            }
1098            if c == b'"' {
1099                let end = self.i;
1100                self.i += 1; // consume closing quote
1101                // Safety: start and end are at UTF-8 code point boundaries (quotes),
1102                // so slicing is valid even if contents contain non-ASCII.
1103                return Ok(&self.s[start..end]);
1104            }
1105            self.i += 1;
1106        }
1107        Err("unterminated string")
1108    }
1109
1110    fn parse_string(&mut self) -> Result<String, Cow<'static, str>> {
1111        self.skip_ws();
1112        if self.peek() != Some(b'"') {
1113            return Err(format!(
1114                "expected string got: '{}'",
1115                String::from_utf8_lossy(&self.b[self.i..])
1116            )
1117            .into());
1118        }
1119        let start = self.i + 1;
1120        let mut i = start;
1121        let len = self.b.len();
1122
1123        // First pass: detect if we need to unescape
1124        let mut needs_unescape = false;
1125        while i < len {
1126            let c = self.b[i];
1127            if c == b'\\' {
1128                needs_unescape = true;
1129                break;
1130            }
1131            if c == b'"' {
1132                // no escapes
1133                let s = core::str::from_utf8(&self.b[start..i]).map_err(|_| "utf8 error")?;
1134                self.i = i + 1;
1135                return Ok(s.to_owned());
1136            }
1137            i += 1;
1138        }
1139        if !needs_unescape {
1140            return Err("unterminated string".into());
1141        }
1142
1143        // Second pass: build with unescape
1144        let mut out = String::with_capacity((i - start) + 16);
1145        let mut seg_start = start;
1146        while i < len {
1147            let c = self.b[i];
1148            if c == b'\\' {
1149                // push preceding segment
1150                if i > seg_start {
1151                    out.push_str(
1152                        core::str::from_utf8(&self.b[seg_start..i]).map_err(|_| "utf8 error")?,
1153                    );
1154                }
1155                i += 1;
1156                if i >= len {
1157                    return Err("bad escape".into());
1158                }
1159                let e = self.b[i];
1160                match e {
1161                    b'"' => out.push('"'),
1162                    b'\\' => out.push('\\'),
1163                    b'/' => out.push('/'),
1164                    b'b' => out.push('\u{0008}'),
1165                    b'f' => out.push('\u{000C}'),
1166                    b'n' => out.push('\n'),
1167                    b'r' => out.push('\r'),
1168                    b't' => out.push('\t'),
1169                    b'u' => {
1170                        let (cp, new_i) = self.parse_u_escape(i + 1)?;
1171                        i = new_i - 1; // -1 because loop will i += 1 at end
1172                        if let Some(ch) = core::char::from_u32(cp) {
1173                            out.push(ch);
1174                        } else {
1175                            return Err("invalid unicode".into());
1176                        }
1177                    }
1178                    _ => return Err("bad escape".into()),
1179                }
1180                i += 1;
1181                seg_start = i;
1182                continue;
1183            } else if c == b'"' {
1184                // end
1185                if i > seg_start {
1186                    out.push_str(
1187                        core::str::from_utf8(&self.b[seg_start..i]).map_err(|_| "utf8 error")?,
1188                    );
1189                }
1190                self.i = i + 1;
1191                return Ok(out);
1192            } else {
1193                i += 1;
1194            }
1195        }
1196        Err("unterminated string".into())
1197    }
1198
1199    // Parses \uXXXX (with surrogate-pair handling). Input index points at first hex digit after 'u'.
1200    fn parse_u_escape(&self, i: usize) -> Result<(u32, usize), &'static str> {
1201        fn hex4(bytes: &[u8], i: usize) -> Result<(u16, usize), &'static str> {
1202            let end = i + 4;
1203            if end > bytes.len() {
1204                return Err("short \\u");
1205            }
1206            let mut v: u16 = 0;
1207            for b in bytes.iter().take(end).skip(i) {
1208                v = (v << 4) | hex_val(*b)?;
1209            }
1210            Ok((v, end))
1211        }
1212        fn hex_val(b: u8) -> Result<u16, &'static str> {
1213            match b {
1214                b'0'..=b'9' => Ok((b - b'0') as u16),
1215                b'a'..=b'f' => Ok((b - b'a' + 10) as u16),
1216                b'A'..=b'F' => Ok((b - b'A' + 10) as u16),
1217                _ => Err("bad hex"),
1218            }
1219        }
1220
1221        let (first, i2) = hex4(self.b, i)?;
1222        let cp = first as u32;
1223
1224        // Surrogate pair handling
1225        if (0xD800..=0xDBFF).contains(&first) {
1226            // Expect \uXXXX next
1227            if i2 + 2 > self.b.len() || self.b[i2] != b'\\' || self.b[i2 + 1] != b'u' {
1228                return Err("missing low surrogate");
1229            }
1230            let (second, i3) = hex4(self.b, i2 + 2)?;
1231            if !(0xDC00..=0xDFFF).contains(&second) {
1232                return Err("invalid low surrogate");
1233            }
1234            let high = (first as u32) - 0xD800;
1235            let low = (second as u32) - 0xDC00;
1236            let code = 0x10000 + ((high << 10) | low);
1237            Ok((code, i3))
1238        } else if (0xDC00..=0xDFFF).contains(&first) {
1239            Err("unpaired low surrogate")
1240        } else {
1241            Ok((cp, i2))
1242        }
1243    }
1244
1245    fn parse_opt_string(&mut self) -> Result<Option<String>, Cow<'static, str>> {
1246        if self.peek_is_null() {
1247            self.parse_null()?;
1248            Ok(None)
1249        } else {
1250            let s = self.parse_string()?;
1251            Ok(Some(s))
1252        }
1253    }
1254
1255    // Returns a slice of the next JSON value and advances past it.
1256    fn value_slice(&mut self) -> Result<&'a str, &'static str> {
1257        self.skip_ws();
1258        let start = self.i;
1259        let end = self.find_value_end()?;
1260        let out = &self.s[start..end];
1261        self.i = end;
1262        Ok(out)
1263    }
1264
1265    // Skips the next JSON value (string/number/boolean/null/object/array).
1266    fn skip_value(&mut self) -> Result<(), &'static str> {
1267        let _ = self.value_slice()?;
1268        Ok(())
1269    }
1270
1271    fn find_value_end(&mut self) -> Result<usize, &'static str> {
1272        if self.eof() {
1273            return Err("unexpected end");
1274        }
1275        match self.b[self.i] {
1276            b'"' => self.scan_string_end(),
1277            b'{' => self.scan_brace_block(b'{', b'}'),
1278            b'[' => self.scan_brace_block(b'[', b']'),
1279            b't' => {
1280                if self.starts_with_bytes(b"true") {
1281                    Ok(self.i + 4)
1282                } else {
1283                    Err("bad literal")
1284                }
1285            }
1286            b'f' => {
1287                if self.starts_with_bytes(b"false") {
1288                    Ok(self.i + 5)
1289                } else {
1290                    Err("bad literal")
1291                }
1292            }
1293            b'n' => {
1294                if self.starts_with_bytes(b"null") {
1295                    Ok(self.i + 4)
1296                } else {
1297                    Err("bad literal")
1298                }
1299            }
1300            b'-' | b'0'..=b'9' => self.scan_number_end(),
1301            _ => Err("unexpected token"),
1302        }
1303    }
1304
1305    fn scan_string_end(&self) -> Result<usize, &'static str> {
1306        let mut i = self.i + 1;
1307        let len = self.b.len();
1308        let mut escaped = false;
1309        while i < len {
1310            let c = self.b[i];
1311            if escaped {
1312                escaped = false;
1313                i += 1;
1314                continue;
1315            }
1316            if c == b'\\' {
1317                escaped = true;
1318                i += 1;
1319                continue;
1320            }
1321            if c == b'"' {
1322                return Ok(i + 1);
1323            }
1324            i += 1;
1325        }
1326        Err("unterminated string")
1327    }
1328
1329    fn scan_brace_block(&self, open: u8, close: u8) -> Result<usize, &'static str> {
1330        let mut i = self.i;
1331        let len = self.b.len();
1332        let mut depth = 0usize;
1333        while i < len {
1334            let c = self.b[i];
1335            if c == b'"' {
1336                // Skip string
1337                let p = Parser {
1338                    s: self.s,
1339                    b: self.b,
1340                    i,
1341                };
1342                i = p.scan_string_end()?; // returns position after closing "
1343                continue;
1344            }
1345            if c == open {
1346                depth += 1;
1347            } else if c == close {
1348                depth -= 1;
1349                if depth == 0 {
1350                    return Ok(i + 1);
1351                }
1352            }
1353            i += 1;
1354        }
1355        Err("unterminated structure")
1356    }
1357
1358    fn scan_number_end(&self) -> Result<usize, &'static str> {
1359        let len = self.b.len();
1360        let mut i = self.i;
1361
1362        if self.b[i] == b'-' {
1363            i += 1;
1364            if i >= len {
1365                return Err("bad number");
1366            }
1367        }
1368
1369        // int part
1370        match self.b[i] {
1371            b'0' => {
1372                i += 1;
1373            }
1374            b'1'..=b'9' => {
1375                i += 1;
1376                while i < len {
1377                    match self.b[i] {
1378                        b'0'..=b'9' => i += 1,
1379                        _ => break,
1380                    }
1381                }
1382            }
1383            _ => return Err("bad number"),
1384        }
1385
1386        // frac
1387        if i < len && self.b[i] == b'.' {
1388            i += 1;
1389            if i >= len || !self.b[i].is_ascii_digit() {
1390                return Err("bad number");
1391            }
1392            while i < len && self.b[i].is_ascii_digit() {
1393                i += 1;
1394            }
1395        }
1396
1397        // exp
1398        if i < len && (self.b[i] == b'e' || self.b[i] == b'E') {
1399            i += 1;
1400            if i < len && (self.b[i] == b'+' || self.b[i] == b'-') {
1401                i += 1;
1402            }
1403            if i >= len || !self.b[i].is_ascii_digit() {
1404                return Err("bad number");
1405            }
1406            while i < len && self.b[i].is_ascii_digit() {
1407                i += 1;
1408            }
1409        }
1410
1411        Ok(i)
1412    }
1413}
1414
1415#[cfg(test)]
1416mod tests {
1417    extern crate alloc;
1418    use alloc::string::ToString;
1419
1420    use super::*;
1421    use crate::LastData;
1422
1423    #[test]
1424    fn rp1() {
1425        let cfg = ReasoningConfig::from_json(r#"{"enabled": false}"#).unwrap();
1426        assert!(!cfg.enabled);
1427        assert!(cfg.effort.is_none());
1428        assert!(cfg.tokens.is_none());
1429    }
1430
1431    #[test]
1432    fn rp2() {
1433        let cfg = ReasoningConfig::from_json(r#"{"enabled": true, "effort": "medium"}"#).unwrap();
1434        assert!(cfg.enabled);
1435        assert_eq!(cfg.effort, Some(ReasoningEffort::Medium));
1436        assert!(cfg.tokens.is_none());
1437    }
1438
1439    #[test]
1440    fn rp3() {
1441        let cfg = ReasoningConfig::from_json(r#"{"enabled": true, "tokens": 2048}"#).unwrap();
1442        assert!(cfg.enabled);
1443        assert_eq!(cfg.tokens, Some(2048));
1444        assert!(cfg.effort.is_none());
1445    }
1446
1447    #[test]
1448    fn rp4() {
1449        let cfg = ReasoningConfig::from_json(r#"{"enabled":true,"effort":"high","tokens":null}"#)
1450            .unwrap();
1451        assert!(cfg.enabled);
1452        assert_eq!(cfg.effort, Some(ReasoningEffort::High));
1453        assert!(cfg.tokens.is_none());
1454    }
1455
1456    #[test]
1457    fn cpo1() {
1458        let s = r#"
1459 {
1460     "prompt": "\n\nExample JSON 1: {\"enabled\": false}\n",
1461     "model": "google/gemma-3n-e4b-it:free",
1462     "system": "Make your answer concise but complete. No yapping. Direct professional tone. No emoji.",
1463     "show_reasoning": false,
1464     "reasoning": { "enabled": false },
1465     "merge_config": true
1466 }
1467 "#;
1468        let opts = PromptOpts::from_json(s).unwrap();
1469        assert!(!opts.show_reasoning.unwrap());
1470        assert_eq!(opts.models, vec!["google/gemma-3n-e4b-it:free"]);
1471        assert!(!opts.reasoning.unwrap().enabled);
1472        assert!(opts.merge_config);
1473    }
1474
1475    #[test]
1476    fn cpo2() {
1477        let s = r#"
1478    {"model":"openai/gpt-5","provider":"openai","system":"Make your answer concise but complete. No yapping. Direct professional tone. No emoji.","priority":null,"reasoning":{"enabled":true,"effort":"high","tokens":null},"show_reasoning":false,"quiet":true}
1479    "#;
1480        let opts = PromptOpts::from_json(s).unwrap();
1481        assert!(!opts.show_reasoning.unwrap());
1482        assert_eq!(opts.models, vec!["openai/gpt-5"]);
1483        assert!(opts.reasoning.as_ref().unwrap().enabled);
1484        assert_eq!(
1485            opts.reasoning.as_ref().unwrap().effort,
1486            Some(ReasoningEffort::High)
1487        );
1488    }
1489
1490    #[test]
1491    fn last_data() {
1492        let s = r#"
1493{"opts":{"model":"google/gemma-3n-e4b-it:free","provider":"google-ai-studio","system":"Make your answer concise but complete. No yapping. Direct professional tone. No emoji.","priority":null,"reasoning":{"enabled":false,"effort":null,"tokens":null},"show_reasoning":false},"messages":[{"role":"user","content":"Hello"},{"role":"assistant","content":"Hello there! 😊How can I help you today? I'm ready for anything – questions, stories, ideas, or just a friendly chat!Let me know what's on your mind. ✨"}]}
1494"#;
1495        let l = LastData::from_json(s).unwrap();
1496        assert_eq!(l.opts.provider.as_deref(), Some("google-ai-studio"));
1497        assert_eq!(l.messages.len(), 2);
1498    }
1499
1500    #[test]
1501    fn test_usage() {
1502        let s = r#"{"prompt_tokens":42,"completion_tokens":2,"total_tokens":44,"cost":0.0534,"is_byok":false,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"cost_details":{"upstream_inference_cost":null,"upstream_inference_prompt_cost":0,"upstream_inference_completions_cost":0},"completion_tokens_details":{"reasoning_tokens":0,"image_tokens":0}}"#;
1503        let usage = Usage::from_json(s).unwrap();
1504        assert_eq!(usage.cost, 0.0534);
1505    }
1506
1507    #[test]
1508    fn test_choice() {
1509        let s = r#"{"index":0,"delta":{"role":"assistant","content":"Hello"},"finish_reason":"stop","native_finish_reason":"stop","logprobs":null}"#;
1510        let choice = Choice::from_json(s).unwrap();
1511        assert_eq!(choice.delta.content.as_deref(), Some("Hello"));
1512    }
1513
1514    #[test]
1515    fn test_chat_completions_response_simple() {
1516        let arr = [
1517            r#"{"id":"gen-1756743299-7ytIBcjALWQQShwMQfw9","provider":"Meta","model":"meta-llama/llama-3.3-8b-instruct:free","object":"chat.completion.chunk","created":1756743300,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}"#,
1518            r#"{"id":"gen-1756743299-7ytIBcjALWQQShwMQfw9","provider":"Meta","model":"meta-llama/llama-3.3-8b-instruct:free","object":"chat.completion.chunk","created":1756743300,"choices":[{"index":0,"delta":{"role":"assistant","content":"Hello"},"finish_reason":"stop","native_finish_reason":"stop","logprobs":null}]}"#,
1519            r#"{"id":"gen-1756743299-7ytIBcjALWQQShwMQfw9","provider":"Meta","model":"meta-llama/llama-3.3-8b-instruct:free","object":"chat.completion.chunk","created":1756743300,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"usage":{"prompt_tokens":42,"completion_tokens":2,"total_tokens":44,"cost":0,"is_byok":false,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"cost_details":{"upstream_inference_cost":null,"upstream_inference_prompt_cost":0,"upstream_inference_completions_cost":0},"completion_tokens_details":{"reasoning_tokens":0,"image_tokens":0}}}"#,
1520        ];
1521        for a in arr {
1522            let ccr = ChatCompletionsResponse::from_json(a).unwrap();
1523            assert_eq!(ccr.provider.as_deref(), Some("Meta"));
1524            assert_eq!(
1525                ccr.model.as_deref(),
1526                Some("meta-llama/llama-3.3-8b-instruct:free")
1527            );
1528            assert_eq!(ccr.choices.len(), 1);
1529        }
1530    }
1531
1532    #[test]
1533    fn test_chat_completions_response_more() {
1534        let arr = [
1535            r#"{"id":"gen-1756749262-liysSWPMM37eb25U5gXO","provider":"WandB","model":"deepseek/deepseek-chat-v3.1","object":"chat.completion.chunk","created":1756749262,"choices":[{"index":0,"delta":{"role":"assistant","content":"","reasoning":null,"reasoning_details":[]},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}"#,
1536            r#"{"id":"gen-1756749262-liysSWPMM37eb25U5gXO","provider":"WandB","model":"deepseek/deepseek-chat-v3.1","object":"chat.completion.chunk","created":1756749262,"choices":[{"index":0,"delta":{"role":"assistant","content":"","reasoning":null,"reasoning_details":[]},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}"#,
1537            r#"{"id":"gen-1756749262-liysSWPMM37eb25U5gXO","provider":"WandB","model":"deepseek/deepseek-chat-v3.1","object":"chat.completion.chunk","created":1756749262,"choices":[{"index":0,"delta":{"role":"assistant","content":"","reasoning":null,"reasoning_details":[]},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}"#,
1538            r#"{"id":"gen-1756749262-liysSWPMM37eb25U5gXO","provider":"WandB","model":"deepseek/deepseek-chat-v3.1","object":"chat.completion.chunk","created":1756749262,"choices":[{"index":0,"delta":{"role":"assistant","content":"","reasoning":null,"reasoning_details":[]},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}"#,
1539            r#"{"id":"gen-1756749262-liysSWPMM37eb25U5gXO","provider":"WandB","model":"deepseek/deepseek-chat-v3.1","object":"chat.completion.chunk","created":1756749262,"choices":[{"index":0,"delta":{"role":"assistant","content":"","reasoning":null,"reasoning_details":[]},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}"#,
1540            r#"{"id":"gen-1756749262-liysSWPMM37eb25U5gXO","provider":"WandB","model":"deepseek/deepseek-chat-v3.1","object":"chat.completion.chunk","created":1756749262,"choices":[{"index":0,"delta":{"role":"assistant","content":"Rea","reasoning":null,"reasoning_details":[]},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}"#,
1541            r#"{"id":"gen-1756749262-liysSWPMM37eb25U5gXO","provider":"WandB","model":"deepseek/deepseek-chat-v3.1","object":"chat.completion.chunk","created":1756749262,"choices":[{"index":0,"delta":{"role":"assistant","content":"l","reasoning":null,"reasoning_details":[]},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}"#,
1542            r#"{"id":"gen-1756749262-liysSWPMM37eb25U5gXO","provider":"WandB","model":"deepseek/deepseek-chat-v3.1","object":"chat.completion.chunk","created":1756749262,"choices":[{"index":0,"delta":{"role":"assistant","content":" Madrid, 14 times.","reasoning":null,"reasoning_details":[]},"finish_reason":"stop","native_finish_reason":"stop","logprobs":null}]}"#,
1543            r#"{"id":"gen-1756749262-liysSWPMM37eb25U5gXO","provider":"WandB","model":"deepseek/deepseek-chat-v3.1","object":"chat.completion.chunk","created":1756749262,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"usage":{"prompt_tokens":33,"completion_tokens":8,"total_tokens":41,"cost":0.0000310365,"is_byok":false,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"cost_details":{"upstream_inference_cost":null,"upstream_inference_prompt_cost":0.00001815,"upstream_inference_completions_cost":0.0000132},"completion_tokens_details":{"reasoning_tokens":0,"image_tokens":0}}}"#,
1544        ];
1545        for a in arr {
1546            let ccr = ChatCompletionsResponse::from_json(a).unwrap();
1547            assert_eq!(ccr.provider.as_deref(), Some("WandB"));
1548            assert_eq!(ccr.model.as_deref(), Some("deepseek/deepseek-chat-v3.1"));
1549            assert_eq!(ccr.choices.len(), 1);
1550        }
1551    }
1552
1553    #[test]
1554    fn api_key() {
1555        let s = r#"{"name":"openrouter","value":"sk-or-v1-a123b456c789d012a345b8032470394876576573242374098174093274abcdef"}"#;
1556        let got = config::ApiKey::from_json(s).unwrap();
1557        let expect = config::ApiKey::new(
1558            "openrouter".to_string(),
1559            "sk-or-v1-a123b456c789d012a345b8032470394876576573242374098174093274abcdef".to_string(),
1560        );
1561        assert_eq!(got, expect);
1562    }
1563
1564    #[test]
1565    fn settings() {
1566        let s = r#"{
1567    "save_to_file": true,
1568    "dns": ["104.18.2.115", "104.18.3.115"]
1569}"#;
1570        let settings = config::Settings::from_json(s).unwrap();
1571        assert!(settings.save_to_file);
1572        assert_eq!(settings.dns, ["104.18.2.115", "104.18.3.115"]);
1573    }
1574
1575    #[test]
1576    fn config_file() {
1577        let s = r#"
1578{
1579    "keys": [{"name": "openrouter", "value": "sk-or-v1-abcd1234"}],
1580    "settings": {
1581        "save_to_file": true,
1582        "dns": ["104.18.2.115", "104.18.3.115"]
1583    },
1584    "prompt_opts": {
1585        "model": "google/gemma-3n-e4b-it:free",
1586        "system": "Make your answer concise but complete. No yapping. Direct professional tone. No emoji.",
1587        "quiet": false,
1588        "show_reasoning": false,
1589        "reasoning": {
1590            "enabled": false
1591        }
1592    }
1593}
1594"#;
1595        let cfg = config::ConfigFile::from_json(s).unwrap();
1596        assert_eq!(cfg.keys.len(), 1);
1597        assert!(cfg.settings.is_some());
1598        assert!(cfg.prompt_opts.is_some());
1599    }
1600}