Files
simple-ai-provider/src/providers/openwebui-http.ts
Jan-Marlon Leibl 3d985a95a8 refactor: split OpenWebUI into strategy classes by backend
Replace Conditional with Polymorphism from the Refactoring Guru catalog.

The OpenWebUIProvider previously contained two parallel implementations
(completeWithChat / completeWithOllama, streamWithChat / streamWithOllama,
plus branching in validateConnection) selected by a `useOllamaProxy`
boolean. The 987-line file is split into:

  - openwebui-types.ts       wire-format response types
  - openwebui-http.ts        shared HTTP client (auth, timeout)
  - openwebui-strategies.ts  OpenWebUIStrategy interface plus
                             OpenWebUIChatStrategy and
                             OpenWebUIOllamaStrategy implementations
  - openwebui.ts             thin provider that picks one strategy at
                             construction and delegates

OpenWebUIProvider and OpenWebUIConfig (including useOllamaProxy) stay
in their original locations, so consumer imports are unchanged.

Side effects:
- Error mapping now goes through the base mapProviderError / providerErrorMessages
  hooks added in R2, removing handleOpenWebUIError and its duplicated status switch.
- providerInfo.version bumped to 2.0.0 to reflect the rewrite.
2026-05-21 13:51:08 +02:00

60 lines
1.7 KiB
TypeScript

import { AIProviderError, AIErrorType } from '../types/index.js';
import { DEFAULT_TIMEOUT_MS } from '../constants.js';
export interface OpenWebUIHttpOptions {
baseUrl: string;
apiKey?: string;
timeout?: number;
dangerouslyAllowInsecureConnections?: boolean;
}
/**
* Thin HTTP client shared by OpenWebUI strategies.
* Handles auth header, request body serialization, and timeout-to-AIProviderError translation.
*/
export class OpenWebUIHttpClient {
readonly baseUrl: string;
private readonly apiKey: string | undefined;
private readonly timeout: number;
private readonly dangerouslyAllowInsecureConnections: boolean;
constructor(options: OpenWebUIHttpOptions) {
this.baseUrl = options.baseUrl;
this.apiKey = options.apiKey;
this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
this.dangerouslyAllowInsecureConnections = options.dangerouslyAllowInsecureConnections ?? true;
}
async request(path: string, method: string, body?: unknown): Promise<Response> {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
'User-Agent': 'simple-ai-provider/2.0.0'
};
if (this.apiKey) {
headers['Authorization'] = `Bearer ${this.apiKey}`;
}
const requestOptions: RequestInit = {
method,
headers,
body: body !== undefined ? JSON.stringify(body) : undefined,
signal: AbortSignal.timeout(this.timeout)
};
try {
return await fetch(`${this.baseUrl}${path}`, requestOptions);
} catch (error: any) {
if (error.name === 'AbortError') {
throw new AIProviderError(
'Request timed out',
AIErrorType.TIMEOUT,
undefined,
error
);
}
throw error;
}
}
}