|
立刻注册账号,享受更清爽的界面!
您需要 登录 才可以下载或查看,没有账号?注册
×
研究了一下 https://docs.julep.ai/introduction/julep
支持十几种模型,没有任何限制,模型还蛮多的
- ['mistral-large-2411', 'o1', 'text-embedding-3-large', 'vertex_ai/text-embedding-004', 'claude-3.5-haiku', 'cerebras/llama-4-scout-17b-16e-instruct', 'llama-3.1-8b', 'magnum-v4-72b', 'voyage-multilingual-2', 'claude-3-haiku', 'gpt-4o', 'BAAI/bge-m3', 'openrouter/meta-llama/llama-4-maverick', 'openrouter/meta-llama/llama-4-scout', 'claude-3.5-sonnet', 'hermes-3-llama-3.1-70b', 'claude-3.5-sonnet-20240620', 'qwen-2.5-72b-instruct', 'l3.3-euryale-70b', 'gpt-4o-mini', 'cerebras/llama-3.3-70b', 'o1-preview', 'gemini-1.5-pro-latest', 'l3.1-euryale-70b', 'claude-3-sonnet', 'Alibaba-NLP/gte-large-en-v1.5', 'openrouter/meta-llama/llama-4-scout:free', 'llama-3.1-70b', 'eva-qwen-2.5-72b', 'claude-3.5-sonnet-20241022', 'gemini-2.0-flash', 'deepseek-chat', 'o1-mini', 'eva-llama-3.33-70b', 'gemini-2.5-pro-preview-03-25', 'gemini-1.5-pro', 'gpt-4-turbo', 'openrouter/meta-llama/llama-4-maverick:free', 'o3-mini', 'claude-3.7-sonnet', 'voyage-3', 'cerebras/llama-3.1-8b', 'claude-3-opus']
复制代码
试了下找接口地址组合了一下,但似乎会收到 Internal Server Error?上代码!
- import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
- const JULEP_API_BASE_URL = "https://api.julep.ai/api";
- // Simple function to generate a UUID (for Agent and Session creation)
- function generateUuid(): string {
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
- const r = Math.random() * 16 | 0, v = c === "x" ? r : (r & 0x3 | 0x8);
- return v.toString(16);
- });
- }
- // List of supported models (from your provided list)
- const supportedModels = [
- 'mistral-large-2411', 'o1', 'text-embedding-3-large', 'vertex_ai/text-embedding-004',
- 'claude-3.5-haiku', 'cerebras/llama-4-scout-17b-16e-instruct', 'llama-3.1-8b',
- 'magnum-v4-72b', 'voyage-multilingual-2', 'claude-3-haiku', 'gpt-4o', 'BAAI/bge-m3',
- 'openrouter/meta-llama/llama-4-maverick', 'openrouter/meta-llama/llama-4-scout',
- 'claude-3.5-sonnet', 'hermes-3-llama-3.1-70b', 'claude-3.5-sonnet-20240620',
- 'qwen-2.5-72b-instruct', 'l3.3-euryale-70b', 'gpt-4o-mini', 'cerebras/llama-3.3-70b',
- 'o1-preview', 'gemini-1.5-pro-latest', 'l3.1-euryale-70b', 'claude-3-sonnet',
- 'Alibaba-NLP/gte-large-en-v1.5', 'openrouter/meta-llama/llama-4-scout:free',
- 'llama-3.1-70b', 'eva-qwen-2.5-72b', 'claude-3.5-sonnet-20241022', 'gemini-2.0-flash',
- 'deepseek-chat', 'o1-mini', 'eva-llama-3.33-70b', 'gemini-2.5-pro-preview-03-25',
- 'gemini-1.5-pro', 'gpt-4-turbo', 'openrouter/meta-llama/llama-4-maverick:free',
- 'o3-mini', 'claude-3.7-sonnet', 'voyage-3', 'cerebras/llama-3.1-8b', 'claude-3-opus'
- ];
- async function handler(req: Request): Promise<Response> {
- const url = new URL(req.url);
- // Handle /v1/models endpoint
- if (url.pathname === "/v1/models") {
- if (req.method !== "GET") {
- return new Response("Method Not Allowed", { status: 405 });
- }
- return listModelsHandler();
- }
- // Handle /v1/chat/completions endpoint
- if (url.pathname === "/v1/chat/completions") {
- if (req.method !== "POST") {
- return new Response("Method Not Allowed", { status: 405 });
- }
- return chatCompletionsHandler(req);
- }
- // Return 404 for other paths
- return new Response("Not Found", { status: 404 });
- }
- // Handler for the /v1/models endpoint
- function listModelsHandler(): Response {
- const models = supportedModels.map(modelId => ({
- id: modelId,
- object: "model",
- created: Math.floor(Date.now() / 1000), // Use current time
- owned_by: "julep-proxy", // Indicate this proxy owns the listing
- // Add other relevant fields if needed, like permission
- }));
- const responseBody = {
- object: "list",
- data: models,
- };
- return new Response(JSON.stringify(responseBody), {
- status: 200,
- headers: {
- "Content-Type": "application/json",
- },
- });
- }
- // Handler for the /v1/chat/completions endpoint
- async function chatCompletionsHandler(req: Request): Promise<Response> {
- const headers = new Headers(req.headers);
- headers.delete("content-length");
- if (!headers.has("Authorization")) {
- return new Response("Authorization header is required.", { status: 401 });
- }
- try {
- const openaiPayload = await req.json();
- // 1. Create a new Agent for this session
- const agentId = generateUuid();
- const createAgentPayload = {
- name: `temp-agent-${agentId}`, // Use a unique name
- about: "Temporary agent created for a chat session."
- // Add other default agent properties if desired
- };
- const createAgentResponse = await fetch(`${JULEP_API_BASE_URL}/agents/${agentId}`, {
- method: "POST",
- headers: headers,
- body: JSON.stringify(createAgentPayload),
- });
- if (!createAgentResponse.ok) {
- console.error("Failed to create agent:", await createAgentResponse.text());
- return new Response("Failed to initialize chat session (agent creation failed).", { status: createAgentResponse.status });
- }
- // const agent = await createAgentResponse.json(); // Agent details if needed
- // 2. Create a new Session using the created Agent
- const sessionId = generateUuid(); // Use UUID for Julep Session ID path parameter
- const createSessionPayload = {
- agent: agentId, // Link the session to the newly created agent
- // Add other default session properties if desired
- // user: "user-id-if-known", // Optionally link to a user
- };
- // If the client provided a model, include it in the session creation request
- if (openaiPayload.model) {
- // Note: Julep's session creation doesn't directly take a model parameter,
- // the model is typically associated with the Agent.
- // If you need to force a specific model, you might need to update the Agent
- // after creation, or rely on the chat request's model parameter.
- // For now, we'll just create the session and let the chat request specify the model.
- }
- const createSessionResponse = await fetch(`${JULEP_API_BASE_URL}/sessions/${sessionId}`, {
- method: "POST",
- headers: headers,
- body: JSON.stringify(createSessionPayload),
- });
- if (!createSessionResponse.ok) {
- console.error("Failed to create session:", await createSessionResponse.text());
- // Clean up the created agent? This adds complexity. For now, just fail.
- return new Response("Failed to initialize chat session (session creation failed).", { status: createSessionResponse.status });
- }
- // const session = await createSessionResponse.json(); // Session details if needed
- // 3. Initiate the chat using the new session ID (which is now a UUID)
- const julepPayload = convertOpenaiToJulep(openaiPayload);
- const julepUrl = `${JULEP_API_BASE_URL}/sessions/${sessionId}/chat`; // Use the UUID session ID here
- const julepResponse = await fetch(julepUrl, {
- method: "POST",
- headers: headers,
- body: JSON.stringify(julepPayload),
- });
- // Handle streaming response
- if (openaiPayload.stream && julepResponse.headers.get("content-type")?.includes("text/event-stream")) {
- const readableStream = new ReadableStream({
- async start(controller) {
- const reader = julepResponse.body?.getReader();
- if (!reader) {
- controller.error("Failed to get reader from Julep response.");
- return;
- }
- const decoder = new TextDecoder();
- let buffer = "";
- try {
- while (true) {
- const { done, value } = await reader.read();
- if (done) break;
- buffer += decoder.decode(value, { stream: true });
- // Process complete lines (events)
- const lines = buffer.split('\n');
- buffer = lines.pop() || ""; // Keep the last potentially incomplete line
- for (const line of lines) {
- if (line.startsWith("data:")) {
- const data = line.substring(5).trim(); // Extract data after "data:"
- if (data === "[DONE]") {
- controller.enqueue(`data: [DONE]\n\n`);
- } else {
- try {
- const julepChunk = JSON.parse(data);
- // Use the Julep session ID (UUID) as the OpenAI response ID
- const openaiChunk = convertJulepChunkToOpenai(julepChunk, openaiPayload.model || "julep-model", sessionId);
- controller.enqueue(`data: ${JSON.stringify(openaiChunk)}\n\n`);
- } catch (parseError) {
- console.error("Error parsing Julep stream chunk:", parseError);
- // Optionally enqueue an error chunk or log it
- }
- }
- } else if (line.startsWith(":")) {
- // Ignore comments
- } else {
- // Handle other event types if necessary, though "data" is typical for chat streams
- console.warn("Received unexpected stream line:", line);
- }
- }
- }
- } catch (error) {
- console.error("Error reading Julep stream:", error);
- controller.error(error);
- } finally {
- reader.releaseLock();
- controller.close();
- }
- },
- });
- return new Response(readableStream, {
- status: julepResponse.status,
- headers: {
- "Content-Type": "text/event-stream",
- "Cache-Control": "no-cache",
- "Connection": "keep-alive",
- },
- });
- } else {
- // Handle non-streaming response
- const julepData = await julepResponse.json();
- // Use the Julep session ID (UUID) as the OpenAI response ID
- const openaiResponse = convertJulepToOpenai(julepData, openaiPayload.model || "julep-model", sessionId);
- return new Response(JSON.stringify(openaiResponse), {
- status: julepResponse.status,
- headers: {
- "Content-Type": "application/json",
- },
- });
- }
- } catch (error) {
- console.error("Error processing request:", error);
- return new Response("Internal Server Error", { status: 500 });
- }
- }
- function convertOpenaiToJulep(openaiPayload: any): any {
- const julepPayload: any = {
- messages: openaiPayload.messages.map((msg: any) => ({
- role: msg.role,
- content: msg.content,
- name: msg.name,
- tool_call_id: msg.tool_call_id,
- tool_calls: msg.tool_calls ? msg.tool_calls.map((tc: any) => ({
- type: tc.type,
- function: tc.function ? {
- name: tc.function.name,
- arguments: tc.function.arguments
- } : undefined,
- integration: tc.integration,
- system: tc.system,
- api_call: tc.api_call,
- computer_20241022: tc.computer_20241022,
- text_editor_20241022: tc.text_editor_20241022,
- bash_20241022: tc.bash_20241022,
- id: tc.id,
- })) : undefined,
- })),
- tools: openaiPayload.tools,
- tool_choice: openaiPayload.tool_choice,
- recall: openaiPayload.recall,
- save: openaiPayload.save,
- model: openaiPayload.model,
- stream: openaiPayload.stream,
- stop: openaiPayload.stop,
- seed: openaiPayload.seed,
- max_tokens: openaiPayload.max_tokens,
- logit_bias: openaiPayload.logit_bias,
- response_format: openaiPayload.response_format,
- agent: openaiPayload.agent,
- repetition_penalty: openaiPayload.repetition_penalty,
- length_penalty: openaiPayload.length_penalty,
- min_p: openaiPayload.min_p,
- frequency_penalty: openaiPayload.frequency_penalty,
- presence_penalty: openaiPayload.presence_penalty,
- temperature: openaiPayload.temperature,
- top_p: openaiPayload.top_p,
- };
- // Remove session_id from the payload if it was mistakenly included
- delete julepPayload.session_id;
- return julepPayload;
- }
- // Converts a non-streaming Julep response to OpenAI format
- // Added sessionId parameter to use as the OpenAI response ID
- function convertJulepToOpenai(julepData: any, model: string, sessionId: string): any {
- const openaiResponse: any = {
- id: sessionId, // Use the Julep session ID (UUID) as the OpenAI response ID
- object: "chat.completion",
- created: Math.floor(new Date(julepData.created_at).getTime() / 1000),
- model: model,
- choices: julepData.choices.map((choice: any) => ({
- index: choice.index,
- // *** Fix: Extract message content from choice.message.content ***
- message: {
- role: choice.message?.role || "assistant", // Use choice.message.role
- content: choice.message?.content || "", // Use choice.message.content
- tool_calls: choice.message?.tool_calls ? choice.message.tool_calls.map((tc: any) => ({
- id: tc.id,
- type: tc.type,
- function: tc.function ? {
- name: tc.function.name,
- arguments: tc.function.arguments
- } : undefined,
- })) : undefined,
- },
- finish_reason: choice.finish_reason,
- // logprobs are not typically in non-streaming response choices
- })),
- usage: julepData.usage ? {
- prompt_tokens: julepData.usage.prompt_tokens,
- completion_tokens: julepData.usage.completion_tokens,
- total_tokens: julepData.usage.total_tokens,
- } : undefined,
- };
- return openaiResponse;
- }
- // Converts a single Julep streaming chunk to OpenAI streaming format
- function convertJulepChunkToOpenai(julepChunk: any, model: string, sessionId: string): any {
- const openaiChunk: any = {
- id: sessionId,
- object: "chat.completion.chunk",
- created: Math.floor(Date.now() / 1000),
- model: model,
- choices: julepChunk.choices.map((choice: any) => {
- const openaiChoice: any = {
- index: choice.index,
- // Delta structure for streaming chunks
- delta: {
- role: choice.delta?.role,
- content: choice.delta?.content,
- tool_calls: choice.delta?.tool_calls ? choice.delta.tool_calls.map((tc: any) => ({
- id: tc.id,
- type: tc.type,
- function: tc.function ? {
- name: tc.function.name,
- arguments: tc.function.arguments
- } : undefined,
- })) : undefined,
- },
- finish_reason: choice.finish_reason,
- // logprobs are not typically in streaming chunks
- };
- // Clean up empty delta fields
- if (openaiChoice.delta.role === undefined) delete openaiChoice.delta.role;
- if (openaiChoice.delta.content === undefined) delete openaiChoice.delta.content;
- if (openaiChoice.delta.tool_calls === undefined) delete openaiChoice.delta.tool_calls;
- if (Object.keys(openaiChoice.delta).length === 0 && openaiChoice.finish_reason === undefined) {
- delete openaiChoice.delta;
- }
- return openaiChoice;
- }),
- };
- return openaiChunk;
- }
- // Start the server
- serve(handler);
复制代码
自带 API Key,在 https://dashboard.julep.ai/ 点右下角按钮获取 |
|