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