delta.rs 4.88 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

16
use super::{NvCreateCompletionRequest, NvCreateCompletionResponse};
17
18
use crate::protocols::common;

19
impl NvCreateCompletionRequest {
20
21
22
23
24
25
26
27
    // put this method on the request
    // inspect the request to extract options
    pub fn response_generator(&self) -> DeltaGenerator {
        let options = DeltaGeneratorOptions {
            enable_usage: true,
            enable_logprobs: false,
        };

28
        DeltaGenerator::new(self.inner.model.clone(), options)
29
30
31
32
33
34
35
36
37
38
39
40
41
    }
}

#[derive(Debug, Clone, Default)]
pub struct DeltaGeneratorOptions {
    pub enable_usage: bool,
    pub enable_logprobs: bool,
}

#[derive(Debug, Clone)]
pub struct DeltaGenerator {
    id: String,
    object: String,
42
    created: u32,
43
44
    model: String,
    system_fingerprint: Option<String>,
45
    usage: async_openai::types::CompletionUsage,
46
47
48
49
50
51
52
53
54
55
    options: DeltaGeneratorOptions,
}

impl DeltaGenerator {
    pub fn new(model: String, options: DeltaGeneratorOptions) -> Self {
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_secs();

56
57
58
59
        // SAFETY: Casting from `u64` to `u32` could lead to precision loss after `u32::MAX`,
        // but this will not be an issue until 2106.
        let now: u32 = now.try_into().expect("timestamp exceeds u32::MAX");

60
61
62
63
64
65
66
67
68
69
        // Previously, our home-rolled CompletionUsage impl'd Default
        // PR !387 - https://github.com/64bit/async-openai/pull/387
        let usage = async_openai::types::CompletionUsage {
            completion_tokens: 0,
            prompt_tokens: 0,
            total_tokens: 0,
            completion_tokens_details: None,
            prompt_tokens_details: None,
        };

70
71
72
73
74
75
        Self {
            id: format!("cmpl-{}", uuid::Uuid::new_v4()),
            object: "text_completion".to_string(),
            created: now,
            model,
            system_fingerprint: None,
76
            usage,
77
78
79
80
            options,
        }
    }

81
    pub fn update_isl(&mut self, isl: u32) {
82
83
84
85
86
        self.usage.prompt_tokens = isl;
    }

    pub fn create_choice(
        &self,
87
        index: u32,
88
        text: Option<String>,
89
        finish_reason: Option<async_openai::types::CompletionFinishReason>,
90
    ) -> NvCreateCompletionResponse {
91
92
        // todo - update for tool calling

93
94
95
96
97
        let mut usage = self.usage.clone();
        if self.options.enable_usage {
            usage.total_tokens = usage.prompt_tokens + usage.completion_tokens;
        }

98
        let inner = async_openai::types::CreateCompletionResponse {
99
100
            id: self.id.clone(),
            object: self.object.clone(),
101
            created: self.created,
102
103
            model: self.model.clone(),
            system_fingerprint: self.system_fingerprint.clone(),
104
            choices: vec![async_openai::types::Choice {
105
                text: text.unwrap_or_default(),
106
                index,
107
108
109
110
                finish_reason,
                logprobs: None,
            }],
            usage: if self.options.enable_usage {
111
                Some(usage)
112
113
114
            } else {
                None
            },
115
116
117
        };

        NvCreateCompletionResponse { inner }
118
119
120
    }
}

121
impl crate::protocols::openai::DeltaGeneratorExt<NvCreateCompletionResponse> for DeltaGenerator {
122
123
124
    fn choice_from_postprocessor(
        &mut self,
        delta: common::llm_backend::BackendOutput,
125
    ) -> anyhow::Result<NvCreateCompletionResponse> {
126
127
        // aggregate usage
        if self.options.enable_usage {
128
129
130
131
132
133
134
135
136
            // SAFETY: Casting from `usize` to `u32` could lead to precision loss after `u32::MAX`,
            // but this will not be an issue until context lengths exceed 4_294_967_295.
            let token_length: u32 = delta
                .token_ids
                .len()
                .try_into()
                .expect("token_ids length exceeds u32::MAX");

            self.usage.completion_tokens += token_length;
137
138
        }

139
140
141
        // TODO logprobs

        let finish_reason = delta.finish_reason.map(Into::into);
142
143

        // create choice
144
        let index = delta.index.unwrap_or(0);
145
146
        let response = self.create_choice(index, delta.text.clone(), finish_reason);
        Ok(response)
147
    }
148
149

    fn get_isl(&self) -> Option<u32> {
150
        Some(self.usage.prompt_tokens)
151
    }
152
}