openai-compatible.js 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. /**
  2. * OpenAI-compatible provider — proxies requests to any OpenAI-compatible backend
  3. * (vLLM, Ollama, LiteLLM, etc.) using native fetch().
  4. */
  5. export function createProvider(config) {
  6. const { id, baseUrl, apiKey, models } = config;
  7. // Normalize baseUrl: strip trailing slash, ensure /v1 is present
  8. let normalizedUrl = baseUrl.replace(/\/+$/, '');
  9. if (!normalizedUrl.endsWith('/v1')) {
  10. normalizedUrl += '/v1';
  11. }
  12. const completionsUrl = `${normalizedUrl}/chat/completions`;
  13. function buildHeaders() {
  14. const headers = { 'Content-Type': 'application/json' };
  15. if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;
  16. return headers;
  17. }
  18. function buildBody(messages, modelId, options, stream) {
  19. const providerModelId = models[modelId] || modelId;
  20. const body = { model: providerModelId, messages, stream };
  21. if (options.temperature !== undefined) body.temperature = options.temperature;
  22. if (options.max_tokens !== undefined) body.max_tokens = options.max_tokens;
  23. if (options.top_p !== undefined) body.top_p = options.top_p;
  24. if (options.stop !== undefined) body.stop = options.stop;
  25. return body;
  26. }
  27. async function request(messages, modelId, options, stream) {
  28. const body = buildBody(messages, modelId, options, stream);
  29. const headers = buildHeaders();
  30. let response;
  31. try {
  32. response = await fetch(completionsUrl, {
  33. method: 'POST',
  34. headers,
  35. body: JSON.stringify(body)
  36. });
  37. } catch (error) {
  38. throw new Error(`OpenAI-compatible provider request failed: ${error.message}`);
  39. }
  40. if (!response.ok) {
  41. let errorBody = '';
  42. try { errorBody = await response.text(); } catch { /* ignore */ }
  43. throw new Error(
  44. `OpenAI-compatible provider returned ${response.status}${errorBody ? `: ${errorBody}` : ''}`
  45. );
  46. }
  47. return response;
  48. }
  49. async function chat(messages, modelId, options = {}) {
  50. const response = await request(messages, modelId, options, false);
  51. return response.json();
  52. }
  53. async function chatStream(messages, modelId, options = {}) {
  54. const response = await request(messages, modelId, options, true);
  55. return response.body;
  56. }
  57. return { id, chat, chatStream };
  58. }