Rebuild and update project functionality
This commit is contained in:
@@ -2,4 +2,6 @@ import ModelAdapter from "./model-adapter";
|
||||
|
||||
const adapter = new ModelAdapter();
|
||||
|
||||
adapter["qwenService"]["initialize"]?.();
|
||||
|
||||
export default adapter;
|
||||
|
||||
@@ -70,6 +70,23 @@ export class ModelAdapter {
|
||||
return this.qwenService.getTokenInfo();
|
||||
}
|
||||
|
||||
hasQwenAuth(): boolean {
|
||||
return this.qwenService.hasOAuthToken();
|
||||
}
|
||||
|
||||
private isProviderAuthenticated(provider: ModelProvider): boolean {
|
||||
switch (provider) {
|
||||
case "qwen":
|
||||
return this.hasQwenAuth() || this.qwenService.hasApiKey();
|
||||
case "ollama":
|
||||
return this.ollamaService.hasAuth();
|
||||
case "zai":
|
||||
return this.zaiService.hasAuth();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private buildFallbackProviders(...providers: ModelProvider[]): ModelProvider[] {
|
||||
const seen = new Set<ModelProvider>();
|
||||
return providers.filter((provider) => {
|
||||
@@ -85,13 +102,29 @@ export class ModelAdapter {
|
||||
operation: (service: any) => Promise<APIResponse<T>>,
|
||||
providers: ModelProvider[]
|
||||
): Promise<APIResponse<T>> {
|
||||
console.log("[ModelAdapter] Attempting providers in order:", providers);
|
||||
let lastError: string | null = null;
|
||||
|
||||
for (const provider of providers) {
|
||||
try {
|
||||
console.log(`[ModelAdapter] Checking authentication for ${provider}...`);
|
||||
|
||||
if (!this.isProviderAuthenticated(provider)) {
|
||||
console.log(`[ModelAdapter] Provider ${provider} is not authenticated, skipping`);
|
||||
continue;
|
||||
}
|
||||
|
||||
let service: any;
|
||||
|
||||
console.log(`[ModelAdapter] Trying provider: ${provider}`);
|
||||
|
||||
switch (provider) {
|
||||
case "qwen":
|
||||
service = this.qwenService;
|
||||
console.log("[ModelAdapter] Qwen service:", {
|
||||
hasApiKey: !!this.qwenService["apiKey"],
|
||||
hasToken: !!this.qwenService.getTokenInfo()?.accessToken
|
||||
});
|
||||
break;
|
||||
case "ollama":
|
||||
service = this.ollamaService;
|
||||
@@ -102,17 +135,30 @@ export class ModelAdapter {
|
||||
}
|
||||
|
||||
const result = await operation(service);
|
||||
console.log(`[ModelAdapter] Provider ${provider} result:`, result);
|
||||
|
||||
if (result.success) {
|
||||
console.log(`[ModelAdapter] Success with provider: ${provider}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (result.error) {
|
||||
lastError = result.error;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error with ${provider}:`, error);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.error(`[ModelAdapter] Error with ${provider}:`, errorMessage);
|
||||
lastError = errorMessage || lastError;
|
||||
}
|
||||
}
|
||||
|
||||
const finalError = lastError
|
||||
? `All providers failed: ${lastError}`
|
||||
: "All providers failed. Please configure API key in Settings";
|
||||
console.error(`[ModelAdapter] ${finalError}`);
|
||||
return {
|
||||
success: false,
|
||||
error: "All providers failed",
|
||||
error: finalError,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,10 @@ export class OllamaCloudService {
|
||||
};
|
||||
}
|
||||
|
||||
hasAuth(): boolean {
|
||||
return !!this.config.apiKey;
|
||||
}
|
||||
|
||||
private ensureApiKey(): string {
|
||||
if (this.config.apiKey) {
|
||||
return this.config.apiKey;
|
||||
|
||||
@@ -68,10 +68,20 @@ export class QwenOAuthService {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
hasApiKey(): boolean {
|
||||
return !!this.apiKey;
|
||||
}
|
||||
|
||||
hasOAuthToken(): boolean {
|
||||
return !!this.getTokenInfo()?.accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build default headers for Qwen completions (includes OAuth token refresh).
|
||||
*/
|
||||
private async getRequestHeaders(): Promise<Record<string, string>> {
|
||||
console.log("[QwenOAuth] Getting request headers...");
|
||||
|
||||
const token = await this.getValidToken();
|
||||
const headers: Record<string, string> = {
|
||||
"Content-Type": "application/json",
|
||||
@@ -79,14 +89,17 @@ export class QwenOAuthService {
|
||||
|
||||
if (token?.accessToken) {
|
||||
headers["Authorization"] = `Bearer ${token.accessToken}`;
|
||||
console.log("[QwenOAuth] Using OAuth token for authorization");
|
||||
return headers;
|
||||
}
|
||||
|
||||
if (this.apiKey) {
|
||||
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
||||
console.log("[QwenOAuth] Using API key for authorization");
|
||||
return headers;
|
||||
}
|
||||
|
||||
console.error("[QwenOAuth] No OAuth token or API key available");
|
||||
throw new Error("Please configure a Qwen API key or authenticate via OAuth.");
|
||||
}
|
||||
|
||||
@@ -96,8 +109,11 @@ export class QwenOAuthService {
|
||||
private getEffectiveEndpoint(): string {
|
||||
const resourceUrl = this.token?.resourceUrl;
|
||||
if (resourceUrl) {
|
||||
return this.normalizeResourceUrl(resourceUrl);
|
||||
const normalized = this.normalizeResourceUrl(resourceUrl);
|
||||
console.log("[Qwen] Using resource URL:", normalized);
|
||||
return normalized;
|
||||
}
|
||||
console.log("[Qwen] Using default endpoint:", this.endpoint);
|
||||
return this.endpoint;
|
||||
}
|
||||
|
||||
@@ -109,7 +125,12 @@ export class QwenOAuthService {
|
||||
|
||||
const withProtocol = trimmed.startsWith("http") ? trimmed : `https://${trimmed}`;
|
||||
const cleaned = withProtocol.replace(/\/$/, "");
|
||||
return cleaned.endsWith("/v1") ? cleaned : `${cleaned}/v1`;
|
||||
|
||||
if (cleaned.endsWith("/v1") || cleaned.endsWith("/compatible-mode/v1")) {
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
return `${cleaned}/v1`;
|
||||
}
|
||||
|
||||
private hydrateTokens() {
|
||||
@@ -132,6 +153,7 @@ export class QwenOAuthService {
|
||||
|
||||
private getStoredToken(): QwenOAuthToken | null {
|
||||
this.hydrateTokens();
|
||||
console.log("[QwenOAuth] Retrieved stored token:", this.token ? { hasAccessToken: !!this.token.accessToken, expiresAt: this.token.expiresAt } : null);
|
||||
return this.token;
|
||||
}
|
||||
|
||||
@@ -229,8 +251,18 @@ export class QwenOAuthService {
|
||||
this.storageHydrated = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the service and hydrate tokens from storage.
|
||||
*/
|
||||
initialize(): void {
|
||||
console.log("[QwenOAuth] Initializing service...");
|
||||
this.hydrateTokens();
|
||||
}
|
||||
|
||||
getTokenInfo(): QwenOAuthToken | null {
|
||||
return this.getStoredToken();
|
||||
this.hydrateTokens();
|
||||
console.log("[QwenOAuth] getTokenInfo called, returning:", this.token ? { hasAccessToken: !!this.token.accessToken, expiresAt: this.token.expiresAt } : null);
|
||||
return this.token;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -353,12 +385,30 @@ export class QwenOAuthService {
|
||||
}
|
||||
|
||||
private parseTokenResponse(data: any): QwenOAuthToken {
|
||||
return {
|
||||
console.log("[QwenOAuth] Token response received:", data);
|
||||
|
||||
const token: QwenOAuthToken = {
|
||||
accessToken: data.access_token,
|
||||
refreshToken: data.refresh_token,
|
||||
resourceUrl: data.resource_url,
|
||||
expiresAt: data.expires_in ? Date.now() + data.expires_in * 1000 : undefined,
|
||||
};
|
||||
|
||||
if (data.resource_url) {
|
||||
token.resourceUrl = data.resource_url;
|
||||
console.log("[QwenOAuth] Using resource_url from response:", data.resource_url);
|
||||
} else if (data.endpoint) {
|
||||
token.resourceUrl = data.endpoint;
|
||||
console.log("[QwenOAuth] Using endpoint from response:", data.endpoint);
|
||||
} else if (data.resource_server) {
|
||||
token.resourceUrl = `https://${data.resource_server}/compatible-mode/v1`;
|
||||
console.log("[QwenOAuth] Using resource_server from response:", data.resource_server);
|
||||
} else {
|
||||
console.log("[QwenOAuth] No resource_url/endpoint in response, will use default Qwen endpoint");
|
||||
console.log("[QwenOAuth] Available fields in response:", Object.keys(data));
|
||||
}
|
||||
|
||||
console.log("[QwenOAuth] Parsed token:", { hasAccessToken: !!token.accessToken, hasRefreshToken: !!token.refreshToken, hasResourceUrl: !!token.resourceUrl, expiresAt: token.expiresAt });
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -395,12 +445,19 @@ export class QwenOAuthService {
|
||||
): Promise<APIResponse<string>> {
|
||||
try {
|
||||
const headers = await this.getRequestHeaders();
|
||||
const url = `${this.getEffectiveEndpoint()}/chat/completions`;
|
||||
const baseUrl = this.getEffectiveEndpoint();
|
||||
const url = `${this.oauthBaseUrl}/chat`;
|
||||
|
||||
console.log("[Qwen] Chat completion request:", { url, model, hasAuth: !!headers.Authorization });
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: headers.Authorization || "",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
endpoint: baseUrl,
|
||||
model,
|
||||
messages,
|
||||
stream,
|
||||
@@ -409,6 +466,7 @@ export class QwenOAuthService {
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error("[Qwen] Chat completion failed:", response.status, response.statusText, errorText);
|
||||
throw new Error(`Chat completion failed (${response.status}): ${response.statusText} - ${errorText}`);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,10 @@ export class ZaiPlanService {
|
||||
};
|
||||
}
|
||||
|
||||
hasAuth(): boolean {
|
||||
return !!this.config.apiKey;
|
||||
}
|
||||
|
||||
private getHeaders(): Record<string, string> {
|
||||
return {
|
||||
"Content-Type": "application/json",
|
||||
|
||||
Reference in New Issue
Block a user