返回 oh-my-ppt
agnes-ai.ts
根目录 / src / main / agent-runtime / provider / image / providers / agnes-ai.ts
1 import type {
2 ImageGenerationProviderAdapter,
3 ImageGenerationResult,
4 ResolvedImageModelConfig
5 } from '../types'
6 import log from 'electron-log/main.js'
7 import { collectImageResults, joinUrl, readJsonResponse, readRecord, readString } from './utils'
8 import { resolveConfiguredDefaultImageSize } from './default-size'
9
10 const DEFAULT_BASE_URL = 'https://apihub.agnes-ai.com/v1'
11 const DEFAULT_SIZE = '1024x768'
12
13 const AGNES_SIZE_MAP: Record<string, string> = {
14 '1:1': '1024x1024',
15 '16:9': '1024x768',
16 '4:3': '1024x768'
17 }
18
19 const buildEndpoint = (config: ResolvedImageModelConfig): string => {
20 const endpoint = readString(config.modelConfig, 'endpoint')
21 if (endpoint) return endpoint
22
23 const baseUrl = readString(config.modelConfig, 'baseUrl') || DEFAULT_BASE_URL
24 if (/\/images\/generations$/i.test(baseUrl.replace(/\/+$/, ''))) {
25 return baseUrl.replace(/\/+$/, '')
26 }
27 return joinUrl(baseUrl, '/images/generations')
28 }
29
30 const resolveSize = (config: ResolvedImageModelConfig, inputSize: string): string => {
31 const explicitSize = readString(config.modelConfig, 'size')
32 if (explicitSize) return explicitSize
33 return AGNES_SIZE_MAP[inputSize] || inputSize
34 }
35
36 const buildExtraBody = (config: ResolvedImageModelConfig): Record<string, unknown> | undefined => {
37 const extraBody = { ...readRecord(config.modelConfig.extraBody) }
38 const responseFormat = readString(config.modelConfig, 'responseFormat')
39 if (responseFormat && extraBody.response_format === undefined) {
40 extraBody.response_format = responseFormat
41 }
42 return Object.keys(extraBody).length > 0 ? extraBody : undefined
43 }
44
45 export const agnesAiAdapter: ImageGenerationProviderAdapter = {
46 getDefaultSize(config) {
47 return resolveConfiguredDefaultImageSize(config) || DEFAULT_SIZE
48 },
49
50 async generate(config, input) {
51 const endpoint = buildEndpoint(config)
52 const model = readString(config.modelConfig, 'model')
53 const apiKey = readString(config.modelConfig, 'apiKey')
54 if (!model) throw new Error('Agnes image model is required')
55 if (!apiKey) throw new Error('Agnes API key is required')
56
57 const requestBody = readRecord(config.modelConfig.requestBody)
58 const headers = readRecord(config.modelConfig.headers) as Record<string, string>
59 const size = resolveSize(config, input.size)
60 const requestCount = Math.max(1, input.count)
61 const results: ImageGenerationResult[] = []
62 const startedAt = Date.now()
63
64 log.info('[images:agnes] sync generation start', {
65 model,
66 endpoint,
67 size,
68 requestCount,
69 promptLength: input.prompt.length,
70 hasSeed: typeof input.seed === 'number'
71 })
72
73 for (let i = 0; i < requestCount; i += 1) {
74 const requestStartedAt = Date.now()
75 const extraBody = buildExtraBody(config)
76 const body = {
77 model,
78 prompt: input.prompt,
79 size,
80 ...(typeof input.seed === 'number' ? { seed: input.seed } : {}),
81 ...(extraBody ? { extra_body: extraBody } : {}),
82 ...requestBody
83 }
84 const response = await fetch(endpoint, {
85 method: 'POST',
86 signal: input.signal,
87 headers: {
88 authorization: `Bearer ${apiKey}`,
89 'content-type': 'application/json',
90 ...headers
91 },
92 body: JSON.stringify(body)
93 })
94 const payload = await readJsonResponse(response)
95 const collected = await collectImageResults(payload, input.signal)
96 results.push(...collected)
97 log.info('[images:agnes] sync generation response', {
98 model,
99 requestIndex: i + 1,
100 collectedCount: collected.length,
101 totalCollectedCount: results.length,
102 elapsedMs: Date.now() - requestStartedAt
103 })
104 if (results.length >= input.count) break
105 }
106
107 if (results.length === 0) throw new Error('Agnes image generation returned no images')
108 log.info('[images:agnes] sync generation completed', {
109 model,
110 resultCount: results.length,
111 elapsedMs: Date.now() - startedAt
112 })
113 return results.slice(0, input.count)
114 }
115 }
116
116 lines TYPESCRIPT