forked from boos/BoosAPI
feat(rust): wire downstream-key billing, add /v1/responses, ComfyUI shim
Previously every executor took `_state: AppState` (unused) and `check_auth` discarded the matched downstream key id, so `usage_logs` was orphan code. This change plumbs the key id from auth → handlers → executors → billing, adds the OpenAI Responses API endpoint, and exposes shim aliases under `/api/comfyui-agent/proxy/openai/*` so embedded ComfyUI's OpenAI nodes flow through the same auth/conductor/billing pipeline. - check_auth now returns Option<i64> (managed key id) instead of unit - record_billing helper writes usage_logs after each successful upstream call - extract_usage handles both chat-completions and responses usage shapes - conductor wrappers record billing post-Success; stream variant scans chunks in reverse for the final usage event - POST /v1/responses (stream + non-stream) via ProxyConductor - shim aliases mounted on the same Router so auth/failover/billing work for free - ComfyUIProcess injects BOOSAPI_BUILTIN_KEY env + --comfy-api-base on spawn - bootstrap built-in `comfyui-agent` downstream key at startup (idempotent) - get_auth_header in vendored ComfyUI falls back to BOOSAPI_BUILTIN_KEY env Co-Authored-By: Kiro <noreply@kiro.dev>
This commit is contained in:
@@ -70,9 +70,23 @@ pub async fn execute_chat_completions_stream(
|
||||
_state: AppState,
|
||||
route: UpstreamRoute,
|
||||
body: Value,
|
||||
) -> AppResult<Vec<String>> {
|
||||
execute_streaming_request(route, "/chat/completions", body).await
|
||||
}
|
||||
|
||||
/// Execute a streaming request against any upstream path (e.g. /responses).
|
||||
/// Returns SSE `data: ...` lines verbatim.
|
||||
pub async fn execute_streaming_request(
|
||||
route: UpstreamRoute,
|
||||
path_suffix: &str,
|
||||
body: Value,
|
||||
) -> AppResult<Vec<String>> {
|
||||
let client = http_client();
|
||||
let upstream_url = format!("{}/chat/completions", route.base_url.trim_end_matches('/'));
|
||||
let upstream_url = format!(
|
||||
"{}/{}",
|
||||
route.base_url.trim_end_matches('/'),
|
||||
path_suffix.trim_start_matches('/')
|
||||
);
|
||||
|
||||
let resp = client
|
||||
.post(&upstream_url)
|
||||
@@ -94,7 +108,6 @@ pub async fn execute_chat_completions_stream(
|
||||
)));
|
||||
}
|
||||
|
||||
// Read the streaming response as chunks
|
||||
let mut chunks = Vec::new();
|
||||
let mut stream = resp.bytes_stream();
|
||||
use futures::StreamExt;
|
||||
@@ -102,7 +115,6 @@ pub async fn execute_chat_completions_stream(
|
||||
match chunk_result {
|
||||
Ok(bytes) => {
|
||||
let text = String::from_utf8_lossy(&bytes).to_string();
|
||||
// Parse SSE lines from the chunk
|
||||
for line in text.lines() {
|
||||
if line.starts_with("data: ") {
|
||||
chunks.push(line.to_string());
|
||||
|
||||
Reference in New Issue
Block a user