From 0ce9cc454d57fd5188ae9b8f1315bf050a647645 Mon Sep 17 00:00:00 2001 From: zeke Date: Tue, 26 Nov 2024 15:49:55 +0800 Subject: [PATCH] =?UTF-8?q?add:=20=E6=94=AF=E6=8C=81=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 54 +++++++++++++++++++++++++++++++++++++++++++-- rs-capi/src/main.rs | 43 ++++++++++++++++++++++++++++++++++-- 2 files changed, 93 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3b69751..0552abc 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ - 接口地址:`http://localhost:3000/v1/chat/completions` - 请求方法:POST - 认证方式:Bearer Token(使用 WorkosCursorSessionToken 的值,支持英文逗号分隔的key入参) -- 请求格式和响应格式参考openai +- 请求格式和响应格式参考openai 支持图片!! ## 快速开始 ``` @@ -29,7 +29,57 @@ services: rs-capi: image: ghcr.io/zeke-chin/cursor-api:latest ports: - - 3000:3000 + - 7000:3000 +``` + +调用示例 +``` +curl http://localhost:3000/v1/chat/completions \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer user_xxxx" \ + -d '{ + "model": "gpt-4o", + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "What'\''s in this image? 中文回复" + }, + { + "type": "image_url", + "image_url": { + "url": "https://zh.wikipedia.org/zh-cn/%E7%BE%8E%E5%85%83#/media/File:50_USD_Series_2004_Note_Back.jpg" + } + } + ] + } + ], + "max_tokens": 300 + }' | jq +------------------ +{ + "id": "chatcmpl-30dc37e7-d411-4946-a24d-dcc78ec2fdec", + "object": "chat.completion", + "created": 1732607371, + "model": "gpt-4o", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "这是一张50美元纸币的背面图像。纸币上印有美国国会大厦的图案。" + }, + "finish_reason": "stop" + } + ], + "usage": { + "prompt_tokens": 0, + "completion_tokens": 0, + "total_tokens": 0 + } +} ``` ## 注意事项 diff --git a/rs-capi/src/main.rs b/rs-capi/src/main.rs index 094173d..979d179 100644 --- a/rs-capi/src/main.rs +++ b/rs-capi/src/main.rs @@ -31,7 +31,31 @@ use hex_utils::{chunk_to_utf8_string, string_to_hex}; #[derive(Debug, Deserialize)] struct Message { role: String, - content: String, + content: Vec, +} + +#[derive(Debug, Deserialize)] +#[serde(tag = "type")] +enum ContentPart { + #[serde(rename = "text")] + Text { text: String }, + + #[serde(rename = "image_url")] + ImageUrl { image_url: ImageUrl }, +} + +#[derive(Debug, Deserialize)] +struct ImageUrl { + url: String, +} + +impl std::fmt::Display for ContentPart { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ContentPart::Text { text } => write!(f, "{}", text), + ContentPart::ImageUrl { image_url } => write!(f, "[Image: {}]", image_url.url), + } + } } #[derive(Debug, Deserialize)] @@ -230,10 +254,25 @@ async fn chat_completions( } // 格式化消息 + // let formatted_messages = chat_request + // .messages + // .iter() + // .map(|msg| format!("{}:{}", msg.role, msg.content)) + // .collect::>() + // .join("\n"); + let formatted_messages = chat_request .messages .iter() - .map(|msg| format!("{}:{}", msg.role, msg.content)) + .map(|msg| { + let content = msg + .content + .iter() + .map(|part| part.to_string()) + .collect::>() + .join(", "); + format!("{}:{}", msg.role, content) + }) .collect::>() .join("\n");