refactor: reorganize @shared files

This commit is contained in:
suyao 2026-01-03 05:27:42 +08:00
parent d74506ac3b
commit 78f60deb91
No known key found for this signature in database
33 changed files with 141 additions and 177 deletions

View File

@ -1,15 +0,0 @@
/**
* Shared AI SDK Middlewares
*
* Environment-agnostic middlewares that can be used in both
* renderer process and main process (API server).
*/
export {
buildSharedMiddlewares,
getReasoningTagName,
isGemini3ModelId,
openrouterReasoningMiddleware,
type SharedMiddlewareConfig,
skipGeminiThoughtSignatureMiddleware
} from './middlewares'

View File

@ -1,9 +1,9 @@
/**
* AiHubMix规则集
*/
import type { MinimalModel, MinimalProvider } from '@shared/types'
import { getLowerBaseModelName } from '@shared/utils/naming'
import type { MinimalModel, MinimalProvider } from '../types'
import { provider2Provider, startsWith } from './helper'
import type { RuleSet } from './types'

View File

@ -1,4 +1,5 @@
import type { MinimalModel, MinimalProvider, ProviderType } from '../types'
import type { MinimalModel, MinimalProvider } from '@shared/types'
import { provider2Provider, startsWith } from './helper'
import type { RuleSet } from './types'
@ -9,7 +10,7 @@ const AZURE_ANTHROPIC_RULES: RuleSet = {
match: startsWith('claude'),
provider: (provider: MinimalProvider) => ({
...provider,
type: 'anthropic' as ProviderType,
type: 'anthropic',
apiHost: provider.apiHost + 'anthropic/v1',
id: 'azure-anthropic'
})

View File

@ -1,4 +1,5 @@
import type { MinimalModel, MinimalProvider } from '../types'
import type { MinimalModel, MinimalProvider } from '@shared/types'
import type { RuleSet } from './types'
export const startsWith =

View File

@ -1,7 +1,8 @@
/**
* NewAPI规则集
*/
import type { MinimalModel, MinimalProvider, ProviderType } from '../types'
import type { MinimalModel, MinimalProvider } from '@shared/types'
import { endpointIs, provider2Provider } from './helper'
import type { RuleSet } from './types'
@ -12,7 +13,7 @@ const NEWAPI_RULES: RuleSet = {
provider: (provider) => {
return {
...provider,
type: 'anthropic' as ProviderType
type: 'anthropic'
}
}
},
@ -21,7 +22,7 @@ const NEWAPI_RULES: RuleSet = {
provider: (provider) => {
return {
...provider,
type: 'gemini' as ProviderType
type: 'gemini'
}
}
},
@ -30,7 +31,7 @@ const NEWAPI_RULES: RuleSet = {
provider: (provider) => {
return {
...provider,
type: 'openai-response' as ProviderType
type: 'openai-response'
}
}
},
@ -39,7 +40,7 @@ const NEWAPI_RULES: RuleSet = {
provider: (provider) => {
return {
...provider,
type: 'openai' as ProviderType
type: 'openai'
}
}
}

View File

@ -1,4 +1,4 @@
import type { MinimalModel, MinimalProvider } from '../types'
import type { MinimalModel, MinimalProvider } from '@shared/types'
export interface RuleSet<M extends MinimalModel = MinimalModel, P extends MinimalProvider = MinimalProvider> {
rules: Array<{

View File

@ -1,4 +1,5 @@
import type { MinimalModel, MinimalProvider } from '../types'
import type { MinimalModel, MinimalProvider } from '@shared/types'
import { provider2Provider, startsWith } from './helper'
import type { RuleSet } from './types'

View File

@ -1,7 +1,3 @@
import { getLowerBaseModelName } from '@shared/utils/naming'
import type { MinimalModel } from './types'
export const COPILOT_EDITOR_VERSION = 'vscode/1.104.1'
export const COPILOT_PLUGIN_VERSION = 'copilot-chat/0.26.7'
export const COPILOT_INTEGRATION_ID = 'vscode-chat'
@ -16,11 +12,3 @@ export const COPILOT_DEFAULT_HEADERS = {
'editor-plugin-version': COPILOT_PLUGIN_VERSION,
'copilot-vision-request': 'true'
} as const
// Models that require the OpenAI Responses endpoint when routed through GitHub Copilot (#10560)
const COPILOT_RESPONSES_MODEL_IDS = ['gpt-5-codex', 'gpt-5.1-codex', 'gpt-5.1-codex-mini']
export function isCopilotResponsesModel<M extends MinimalModel>(model: M): boolean {
const normalizedId = getLowerBaseModelName(model.id)
return COPILOT_RESPONSES_MODEL_IDS.some((target) => normalizedId === target)
}

View File

@ -6,6 +6,18 @@
* and how AI SDK expects them.
*/
import type { MinimalProvider } from '@shared/types'
import { SystemProviderIds } from '@shared/types'
import {
isAnthropicProvider,
isAzureOpenAIProvider,
isCherryAIProvider,
isGeminiProvider,
isOllamaProvider,
isPerplexityProvider,
isVertexProvider
} from '@shared/utils/provider'
import {
formatApiHost,
formatAzureOpenAIApiHost,
@ -15,17 +27,6 @@ import {
routeToEndpoint,
withoutTrailingSlash
} from '../utils/url'
import {
isAnthropicProvider,
isAzureOpenAIProvider,
isCherryAIProvider,
isGeminiProvider,
isOllamaProvider,
isPerplexityProvider,
isVertexProvider
} from './detection'
import type { MinimalProvider } from './types'
import { SystemProviderIds } from './types'
/**
* Interface for environment-specific implementations

View File

@ -0,0 +1,23 @@
/**
* Shared Provider Utilities
*
* This module exports utilities for working with AI providers
* that can be shared between main process and renderer process.
*/
// API host formatting
export type { ApiKeyRotator, ProviderFormatContext } from './format'
export {
defaultFormatAzureOpenAIApiHost,
formatProviderApiHost,
getBaseUrlForAiSdk,
simpleKeyRotator
} from './format'
// AI SDK configuration
export type { AiSdkConfig, AiSdkConfigContext } from './providerConfig'
export { providerToAiSdkConfig } from './providerConfig'
// Provider initialization
export { initializeSharedProviders, SHARED_PROVIDER_CONFIGS } from './initialization'
export * from './utils'

View File

@ -6,14 +6,13 @@
*/
import { formatPrivateKey, hasProviderConfig, ProviderConfigFactory } from '@cherrystudio/ai-core/provider'
import { MinimalProvider, SystemProviderIds } from '@shared/types'
import { defaultAppHeaders } from '@shared/utils'
import { isAzureOpenAIProvider, isOllamaProvider } from '@shared/utils/provider'
import { isEmpty } from 'lodash'
import { routeToEndpoint } from '../utils/url'
import { isAzureOpenAIProvider, isOllamaProvider } from './detection'
import { getAiSdkProviderId } from './mapping'
import type { MinimalProvider } from './types'
import { SystemProviderIds } from './types'
import { getAiSdkProviderId } from './utils'
/**
* AI SDK configuration result

View File

@ -6,9 +6,15 @@
*/
import { hasProviderConfigByAlias, type ProviderId, resolveProviderConfigId } from '@cherrystudio/ai-core/provider'
import { MinimalModel, MinimalProvider } from '@shared/types'
import { isAzureOpenAIProvider, isAzureResponsesEndpoint, isNewApiProvider } from '@shared/utils/provider'
import { isAzureOpenAIProvider, isAzureResponsesEndpoint } from './detection'
import type { MinimalProvider } from './types'
import {
aihubmixProviderCreator,
azureAnthropicProviderCreator,
newApiResolverCreator,
vertexAnthropicProviderCreator
} from './config'
/**
* Static mapping from Cherry Studio provider ID/type to AI SDK provider ID
@ -94,3 +100,42 @@ export function getAiSdkProviderId(provider: MinimalProvider): ProviderId {
// 5. 最后的fallback使用provider本身的id
return provider.id
}
export interface ResolveActualProviderOptions<P extends MinimalProvider> {
isSystemProvider?: (provider: P) => boolean
}
const defaultIsSystemProvider = <P extends MinimalProvider>(provider: P): boolean => {
if ('isSystem' in provider) {
return Boolean((provider as unknown as { isSystem?: boolean }).isSystem)
}
return false
}
export function resolveActualProvider<M extends MinimalModel, P extends MinimalProvider>(
provider: P,
model: M,
options: ResolveActualProviderOptions<P> = {}
): P {
let resolvedProvider = provider
if (isNewApiProvider(resolvedProvider)) {
resolvedProvider = newApiResolverCreator(model, resolvedProvider)
}
const isSystemProvider = options.isSystemProvider?.(resolvedProvider) ?? defaultIsSystemProvider(resolvedProvider)
if (isSystemProvider && resolvedProvider.id === 'aihubmix') {
resolvedProvider = aihubmixProviderCreator(model, resolvedProvider)
}
if (isSystemProvider && resolvedProvider.id === 'vertexai') {
resolvedProvider = vertexAnthropicProviderCreator(model, resolvedProvider)
}
if (isAzureOpenAIProvider(resolvedProvider)) {
resolvedProvider = azureAnthropicProviderCreator(model, resolvedProvider)
}
return resolvedProvider
}

View File

@ -1,49 +0,0 @@
/**
* Shared Provider Utilities
*
* This module exports utilities for working with AI providers
* that can be shared between main process and renderer process.
*/
// Type definitions
export type { MinimalProvider, ProviderType, SystemProviderId } from './types'
export { SystemProviderIds } from './types'
// Provider type detection
export {
isAIGatewayProvider,
isAnthropicProvider,
isAwsBedrockProvider,
isAzureOpenAIProvider,
isAzureResponsesEndpoint,
isCherryAIProvider,
isGeminiProvider,
isNewApiProvider,
isOllamaProvider,
isOpenAICompatibleProvider,
isOpenAIProvider,
isPerplexityProvider,
isVertexProvider
} from './detection'
// API host formatting
export type { ApiKeyRotator, ProviderFormatContext } from './format'
export {
defaultFormatAzureOpenAIApiHost,
formatProviderApiHost,
getBaseUrlForAiSdk,
simpleKeyRotator
} from './format'
// Provider ID mapping
export { getAiSdkProviderId, STATIC_PROVIDER_MAPPING, tryResolveProviderId } from './mapping'
// AI SDK configuration
export type { AiSdkConfig, AiSdkConfigContext } from './sdk-config'
export { providerToAiSdkConfig } from './sdk-config'
// Provider resolution
export { resolveActualProvider } from './resolve'
// Provider initialization
export { initializeSharedProviders, SHARED_PROVIDER_CONFIGS } from './initialization'

View File

@ -1,43 +0,0 @@
import { aihubmixProviderCreator, newApiResolverCreator, vertexAnthropicProviderCreator } from './config'
import { azureAnthropicProviderCreator } from './config/azure-anthropic'
import { isAzureOpenAIProvider, isNewApiProvider } from './detection'
import type { MinimalModel, MinimalProvider } from './types'
export interface ResolveActualProviderOptions<P extends MinimalProvider> {
isSystemProvider?: (provider: P) => boolean
}
const defaultIsSystemProvider = <P extends MinimalProvider>(provider: P): boolean => {
if ('isSystem' in provider) {
return Boolean((provider as unknown as { isSystem?: boolean }).isSystem)
}
return false
}
export function resolveActualProvider<M extends MinimalModel, P extends MinimalProvider>(
provider: P,
model: M,
options: ResolveActualProviderOptions<P> = {}
): P {
let resolvedProvider = provider
if (isNewApiProvider(resolvedProvider)) {
resolvedProvider = newApiResolverCreator(model, resolvedProvider)
}
const isSystemProvider = options.isSystemProvider?.(resolvedProvider) ?? defaultIsSystemProvider(resolvedProvider)
if (isSystemProvider && resolvedProvider.id === 'aihubmix') {
resolvedProvider = aihubmixProviderCreator(model, resolvedProvider)
}
if (isSystemProvider && resolvedProvider.id === 'vertexai') {
resolvedProvider = vertexAnthropicProviderCreator(model, resolvedProvider)
}
if (isAzureOpenAIProvider(resolvedProvider)) {
resolvedProvider = azureAnthropicProviderCreator(model, resolvedProvider)
}
return resolvedProvider
}

View File

@ -0,0 +1 @@
export * from './provider'

View File

@ -7,7 +7,8 @@
* NOTE: These functions should match the logic in @renderer/utils/provider.ts
*/
import type { MinimalProvider } from './types'
import type { MinimalModel, MinimalProvider } from '../types'
import { getLowerBaseModelName } from './naming'
/**
* Check if provider is Anthropic type
@ -99,3 +100,11 @@ export function isNewApiProvider<P extends MinimalProvider>(provider: P): boolea
export function isOpenAICompatibleProvider<P extends MinimalProvider>(provider: P): boolean {
return ['openai', 'new-api', 'mistral'].includes(provider.type)
}
// Models that require the OpenAI Responses endpoint when routed through GitHub Copilot (#10560)
const COPILOT_RESPONSES_MODEL_IDS = ['gpt-5-codex', 'gpt-5.1-codex', 'gpt-5.1-codex-mini']
export function isCopilotResponsesModel<M extends MinimalModel>(model: M): boolean {
const normalizedId = getLowerBaseModelName(model.id)
return COPILOT_RESPONSES_MODEL_IDS.some((target) => normalizedId === target)
}

View File

@ -5,7 +5,7 @@
* Used by both main process (API Server) and renderer.
*/
import type { MinimalProvider } from '@shared/provider'
import type { MinimalProvider } from '@shared/types'
import { trim } from 'lodash'
// Supported endpoints for routing

View File

@ -1,7 +1,7 @@
import type { MessageCreateParams } from '@anthropic-ai/sdk/resources'
import { loggerService } from '@logger'
import { buildSharedMiddlewares, type SharedMiddlewareConfig } from '@shared/ai-sdk-middlewares'
import { getAiSdkProviderId } from '@shared/provider'
import { buildSharedMiddlewares, type SharedMiddlewareConfig } from '@shared/aiCore/middlewares'
import { getAiSdkProviderId } from '@shared/aiCore/utils'
import type { Provider } from '@types'
import type { Request, Response } from 'express'
import express from 'express'

View File

@ -18,24 +18,21 @@ import anthropicService from '@main/services/AnthropicService'
import copilotService from '@main/services/CopilotService'
import { reduxService } from '@main/services/ReduxService'
import type { OpenRouterProviderOptions } from '@openrouter/ai-sdk-provider'
import { isGemini3ModelId } from '@shared/ai-sdk-middlewares'
import {
type AiSdkConfig,
type AiSdkConfigContext,
formatProviderApiHost,
initializeSharedProviders,
isAnthropicProvider,
isGeminiProvider,
isOpenAIProvider,
type MinimalProvider,
type ProviderFormatContext,
providerToAiSdkConfig as sharedProviderToAiSdkConfig,
resolveActualProvider,
SystemProviderIds
} from '@shared/provider'
import { COPILOT_DEFAULT_HEADERS } from '@shared/provider/constant'
resolveActualProvider
} from '@shared/aiCore'
import { COPILOT_DEFAULT_HEADERS } from '@shared/aiCore/constant'
import { isGemini3ModelId } from '@shared/aiCore/middlewares'
import type { MinimalProvider } from '@shared/types'
import { defaultAppHeaders } from '@shared/utils'
import type { Provider } from '@types'
import { isAnthropicProvider, isGeminiProvider, isOpenAIProvider } from '@shared/utils/provider'
import { Provider, SystemProviderIds } from '@types'
import type { ImagePart, JSONValue, ModelMessage, Provider as AiSdkProvider, TextPart, Tool as AiSdkTool } from 'ai'
import { simulateStreamingMiddleware, stepCountIs, tool, wrapLanguageModel, zodSchema } from 'ai'
import { net } from 'electron'

View File

@ -5,7 +5,7 @@ import type { MCPTool } from '@renderer/types'
import { type Assistant, type Message, type Model, type Provider, SystemProviderIds } from '@renderer/types'
import type { Chunk } from '@renderer/types/chunk'
import { isOllamaProvider, isSupportEnableThinkingProvider } from '@renderer/utils/provider'
import { openrouterReasoningMiddleware, skipGeminiThoughtSignatureMiddleware } from '@shared/ai-sdk-middlewares'
import { openrouterReasoningMiddleware, skipGeminiThoughtSignatureMiddleware } from '@shared/aiCore/middlewares'
import type { LanguageModelMiddleware } from 'ai'
import { extractReasoningMiddleware, simulateStreamingMiddleware } from 'ai'

View File

@ -4,4 +4,4 @@ export {
azureAnthropicProviderCreator,
newApiResolverCreator,
vertexAnthropicProviderCreator
} from '@shared/provider/config'
} from '@shared/aiCore/config'

View File

@ -1 +1,2 @@
export { COPILOT_DEFAULT_HEADERS, COPILOT_EDITOR_VERSION, isCopilotResponsesModel } from '@shared/provider/constant'
export { COPILOT_DEFAULT_HEADERS, COPILOT_EDITOR_VERSION } from '@shared/aiCore/constant'
export { isCopilotResponsesModel } from '@shared/utils/provider'

View File

@ -1,7 +1,7 @@
import { createProvider as createProviderCore } from '@cherrystudio/ai-core/provider'
import { loggerService } from '@logger'
import type { Provider } from '@renderer/types'
import { getAiSdkProviderId as sharedGetAiSdkProviderId } from '@shared/provider'
import { getAiSdkProviderId as sharedGetAiSdkProviderId } from '@shared/aiCore'
import type { Provider as AiSdkProvider } from 'ai'
import type { AiSdkConfig } from '../types'

View File

@ -18,7 +18,7 @@ import {
type ProviderFormatContext,
providerToAiSdkConfig as sharedProviderToAiSdkConfig,
resolveActualProvider
} from '@shared/provider'
} from '@shared/aiCore'
import { cloneDeep } from 'lodash'
import type { AiSdkConfig } from '../types'

View File

@ -1,5 +1,5 @@
import { loggerService } from '@logger'
import { initializeSharedProviders, SHARED_PROVIDER_CONFIGS } from '@shared/provider'
import { initializeSharedProviders, SHARED_PROVIDER_CONFIGS } from '@shared/aiCore'
const logger = loggerService.withContext('ProviderConfigs')

View File

@ -2,7 +2,7 @@ import { getProviderByModel } from '@renderer/services/AssistantService'
import type { Model } from '@renderer/types'
import { isSystemProviderId } from '@renderer/types'
import { getLowerBaseModelName, isUserSelectedModelType } from '@renderer/utils'
import { isAzureOpenAIProvider } from '@shared/provider'
import { isAzureOpenAIProvider } from '@shared/utils/provider'
import { isEmbeddingModel, isRerankModel } from './embedding'
import { isDeepSeekHybridInferenceModel } from './reasoning'

View File

@ -7,7 +7,7 @@ import type { CSSProperties } from 'react'
export * from './file'
export * from './note'
import type { MinimalModel } from '@shared/provider/types'
import type { MinimalModel } from '@shared/types'
import * as z from 'zod'
import type { StreamTextParams } from './aiCoreTypes'

View File

@ -1,15 +1,16 @@
import type OpenAI from '@cherrystudio/openai'
import type { MinimalProvider } from '@shared/provider'
import type { ProviderType, SystemProviderId, SystemProviderIdTypeMap } from '@shared/provider/types'
import { isSystemProviderId, SystemProviderIds } from '@shared/provider/types'
import {
isSystemProviderId,
MinimalProvider,
ProviderType,
SystemProviderId,
SystemProviderIds,
SystemProviderIdTypeMap
} from '@shared/types'
import type { Model } from '@types'
import type { OpenAIVerbosity } from './aiCoreTypes'
export type { ProviderType } from '@shared/provider'
export type { SystemProviderId, SystemProviderIdTypeMap } from '@shared/provider/types'
export { isSystemProviderId, ProviderTypeSchema, SystemProviderIds } from '@shared/provider/types'
// undefined is treated as supported, enabled by default
export type ProviderApiOptions = {
/** Whether message content of array type is not supported */
@ -159,3 +160,5 @@ export type NotGroqProvider = Provider & {
export const isGroqSystemProvider = (provider: Provider): provider is GroqSystemProvider => {
return provider.id === SystemProviderIds.groq
}
export * from '@shared/types/provider'

View File

@ -15,7 +15,7 @@ export {
isOpenAIProvider,
isPerplexityProvider,
isVertexProvider
} from '@shared/provider'
} from '@shared/utils/provider'
export const getClaudeSupportedProviders = (providers: Provider[]) => {
return providers.filter(