@xinizai/pi-image-gen 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +337 -0
  2. package/dist/core/cache.d.ts +11 -0
  3. package/dist/core/cache.js +42 -0
  4. package/dist/core/cache.js.map +1 -0
  5. package/dist/core/capabilities.d.ts +2 -0
  6. package/dist/core/capabilities.js +65 -0
  7. package/dist/core/capabilities.js.map +1 -0
  8. package/dist/core/errors.d.ts +11 -0
  9. package/dist/core/errors.js +71 -0
  10. package/dist/core/errors.js.map +1 -0
  11. package/dist/core/global-config.d.ts +25 -0
  12. package/dist/core/global-config.js +172 -0
  13. package/dist/core/global-config.js.map +1 -0
  14. package/dist/core/image-service.d.ts +14 -0
  15. package/dist/core/image-service.js +42 -0
  16. package/dist/core/image-service.js.map +1 -0
  17. package/dist/core/model-browser.d.ts +5 -0
  18. package/dist/core/model-browser.js +76 -0
  19. package/dist/core/model-browser.js.map +1 -0
  20. package/dist/core/model-registry.d.ts +10 -0
  21. package/dist/core/model-registry.js +44 -0
  22. package/dist/core/model-registry.js.map +1 -0
  23. package/dist/core/provider.d.ts +2 -0
  24. package/dist/core/provider.js +8 -0
  25. package/dist/core/provider.js.map +1 -0
  26. package/dist/core/types.d.ts +122 -0
  27. package/dist/core/types.js +2 -0
  28. package/dist/core/types.js.map +1 -0
  29. package/dist/index.d.ts +2 -0
  30. package/dist/index.js +490 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/providers/openai-compatible.d.ts +16 -0
  33. package/dist/providers/openai-compatible.js +216 -0
  34. package/dist/providers/openai-compatible.js.map +1 -0
  35. package/dist/tools/shared.d.ts +42 -0
  36. package/dist/tools/shared.js +241 -0
  37. package/dist/tools/shared.js.map +1 -0
  38. package/dist/utils/config.d.ts +5 -0
  39. package/dist/utils/config.js +65 -0
  40. package/dist/utils/config.js.map +1 -0
  41. package/dist/utils/download.d.ts +8 -0
  42. package/dist/utils/download.js +139 -0
  43. package/dist/utils/download.js.map +1 -0
  44. package/dist/utils/files.d.ts +18 -0
  45. package/dist/utils/files.js +83 -0
  46. package/dist/utils/files.js.map +1 -0
  47. package/package.json +34 -0
  48. package/src/core/cache.ts +43 -0
  49. package/src/core/capabilities.ts +55 -0
  50. package/src/core/errors.ts +92 -0
  51. package/src/core/global-config.ts +167 -0
  52. package/src/core/image-service.ts +40 -0
  53. package/src/core/model-browser.ts +73 -0
  54. package/src/core/model-registry.ts +43 -0
  55. package/src/core/provider.ts +8 -0
  56. package/src/core/types.ts +135 -0
  57. package/src/index.ts +394 -0
  58. package/src/providers/openai-compatible.ts +198 -0
  59. package/src/tools/shared.ts +235 -0
  60. package/src/utils/config.ts +61 -0
  61. package/src/utils/download.ts +103 -0
  62. package/src/utils/files.ts +78 -0
@@ -0,0 +1,172 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { mkdir, readFile, rename, writeFile, chmod } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import { dirname, resolve } from "node:path";
5
+ import { ImageGenError } from "./errors.js";
6
+ import { normalizeBaseUrl, parseManualCapabilities } from "../utils/config.js";
7
+ export const GLOBAL_DIR = resolve(homedir(), ".pi", "agent", "pi-image-gen");
8
+ export const PROVIDERS_FILE = resolve(GLOBAL_DIR, "providers.json");
9
+ export class GlobalConfigStore {
10
+ filePath;
11
+ constructor(filePath = PROVIDERS_FILE) {
12
+ this.filePath = filePath;
13
+ }
14
+ async read() {
15
+ try {
16
+ const parsed = JSON.parse(await readFile(this.filePath, "utf8"));
17
+ if (!Array.isArray(parsed.providers))
18
+ throw new ImageGenError("invalid_json", "全局图片 Provider 配置缺少 providers 数组。", `检查或修复 ${this.filePath}。`);
19
+ const providers = parsed.providers.filter(isStoredProvider).map(normalizeStoredProvider);
20
+ const defaultProviderId = typeof parsed.defaultProviderId === "string" && providers.some((p) => p.id === parsed.defaultProviderId) ? parsed.defaultProviderId : providers[0]?.id;
21
+ return { version: 1, providers, defaultProviderId };
22
+ }
23
+ catch (e) {
24
+ if (e instanceof Error && "code" in e && e.code === "ENOENT")
25
+ return { version: 1, providers: [] };
26
+ throw new ImageGenError("invalid_json", "全局图片 Provider 配置文件无效。", `检查或修复 ${this.filePath}。`);
27
+ }
28
+ }
29
+ async write(config) {
30
+ await mkdir(dirname(this.filePath), { recursive: true });
31
+ const tmp = `${this.filePath}.${process.pid}.tmp`;
32
+ await writeFile(tmp, JSON.stringify(config, null, 2), { encoding: "utf8", mode: 0o600 });
33
+ await chmod(tmp, 0o600).catch(() => undefined);
34
+ await rename(tmp, this.filePath);
35
+ await chmod(this.filePath, 0o600).catch(() => undefined);
36
+ }
37
+ async addProvider(input) {
38
+ const config = await this.read();
39
+ const now = new Date().toISOString();
40
+ const id = stableProviderId(input.id ?? input.name, config.providers.map((p) => p.id));
41
+ const provider = {
42
+ id,
43
+ name: input.name.trim(),
44
+ providerType: "openai-compatible",
45
+ baseUrl: normalizeBaseUrl(input.baseUrl),
46
+ apiKey: input.apiKey,
47
+ defaultModel: input.defaultModel || undefined,
48
+ defaultEditModel: undefined,
49
+ defaultVariationModel: undefined,
50
+ responseFormat: input.responseFormat ?? "auto",
51
+ manualCapabilities: parseManualCapabilities(input.manualCapabilities),
52
+ createdAt: now,
53
+ updatedAt: now,
54
+ apiKeyUpdatedAt: now,
55
+ };
56
+ config.providers.push(provider);
57
+ if (!config.defaultProviderId)
58
+ config.defaultProviderId = provider.id;
59
+ await this.write(config);
60
+ return provider;
61
+ }
62
+ async updateProvider(id, patch) {
63
+ const config = await this.read();
64
+ const idx = config.providers.findIndex((p) => p.id === id);
65
+ if (idx < 0)
66
+ throw new ImageGenError("model_unavailable", `提供商 ${id} 不存在。`, "请运行 /image-config 查看可用提供商。 ");
67
+ const current = config.providers[idx];
68
+ const apiKeyChanged = typeof patch.apiKey === "string" && patch.apiKey !== current.apiKey;
69
+ const next = {
70
+ ...current,
71
+ ...patch,
72
+ baseUrl: patch.baseUrl ? normalizeBaseUrl(patch.baseUrl) : current.baseUrl,
73
+ updatedAt: new Date().toISOString(),
74
+ apiKeyUpdatedAt: apiKeyChanged ? new Date().toISOString() : current.apiKeyUpdatedAt,
75
+ };
76
+ config.providers[idx] = next;
77
+ await this.write(config);
78
+ return next;
79
+ }
80
+ async deleteProvider(id) {
81
+ const config = await this.read();
82
+ config.providers = config.providers.filter((p) => p.id !== id);
83
+ if (config.defaultProviderId === id)
84
+ config.defaultProviderId = config.providers[0]?.id;
85
+ await this.write(config);
86
+ }
87
+ async setDefaultProvider(id) {
88
+ const config = await this.read();
89
+ if (!config.providers.some((p) => p.id === id))
90
+ throw new ImageGenError("model_unavailable", `Provider ${id} 不存在。`, "先添加 Provider。 ");
91
+ config.defaultProviderId = id;
92
+ await this.write(config);
93
+ }
94
+ async resolveProvider(idOrName) {
95
+ const config = await this.read();
96
+ if (idOrName)
97
+ return config.providers.find((p) => p.id === idOrName || p.name.toLowerCase() === idOrName.toLowerCase());
98
+ if (config.defaultProviderId)
99
+ return config.providers.find((p) => p.id === config.defaultProviderId);
100
+ if (config.providers.length === 1)
101
+ return config.providers[0];
102
+ return undefined;
103
+ }
104
+ async toRuntimeConfig(provider, env = process.env) {
105
+ return {
106
+ providerId: provider.id,
107
+ providerName: provider.name,
108
+ baseUrl: provider.baseUrl,
109
+ apiKey: provider.apiKey,
110
+ model: provider.defaultModel,
111
+ editModel: provider.defaultEditModel,
112
+ variationModel: provider.defaultVariationModel,
113
+ responseFormat: provider.responseFormat ?? "auto",
114
+ providerType: provider.providerType,
115
+ timeoutMs: Number(env.IMAGE_TIMEOUT_SECONDS ?? 60) * 1000,
116
+ cacheTtlMs: Number(env.IMAGE_CACHE_TTL_SECONDS ?? 600) * 1000,
117
+ outputDir: env.IMAGE_OUTPUT_DIR ? resolve(env.IMAGE_OUTPUT_DIR) : resolve(homedir(), ".pi", "agent", "generated"),
118
+ maxResponseBytes: Number(env.IMAGE_MAX_RESPONSE_BYTES ?? 8_000_000),
119
+ maxDownloadBytes: Number(env.IMAGE_MAX_DOWNLOAD_BYTES ?? 25_000_000),
120
+ manualCapabilities: { ...provider.manualCapabilities, ...parseManualCapabilities(env.IMAGE_MODEL_CAPABILITIES) },
121
+ apiKeyUpdatedAt: provider.apiKeyUpdatedAt,
122
+ };
123
+ }
124
+ }
125
+ export function envFallbackProvider(env = process.env) {
126
+ if (!env.IMAGE_BASE_URL || !env.IMAGE_API_KEY)
127
+ return undefined;
128
+ const now = new Date().toISOString();
129
+ return {
130
+ id: "env",
131
+ name: "环境变量配置",
132
+ providerType: "openai-compatible",
133
+ baseUrl: normalizeBaseUrl(env.IMAGE_BASE_URL),
134
+ apiKey: env.IMAGE_API_KEY,
135
+ defaultModel: env.IMAGE_MODEL || undefined,
136
+ defaultEditModel: env.IMAGE_EDIT_MODEL || undefined,
137
+ defaultVariationModel: env.IMAGE_VARIATION_MODEL || undefined,
138
+ responseFormat: "auto",
139
+ manualCapabilities: parseManualCapabilities(env.IMAGE_MODEL_CAPABILITIES),
140
+ createdAt: now,
141
+ updatedAt: now,
142
+ apiKeyUpdatedAt: now,
143
+ };
144
+ }
145
+ export async function resolveRuntimeConfig(providerId, env = process.env, store = new GlobalConfigStore()) {
146
+ const provider = await store.resolveProvider(providerId);
147
+ if (provider)
148
+ return store.toRuntimeConfig(provider, env);
149
+ const fallback = envFallbackProvider(env);
150
+ if (fallback)
151
+ return store.toRuntimeConfig(fallback, env);
152
+ throw new ImageGenError("missing_config", "还没有配置图片生成提供商。", "运行 /image-config 添加全局图片提供商。 ");
153
+ }
154
+ function isStoredProvider(value) {
155
+ if (!value || typeof value !== "object")
156
+ return false;
157
+ const p = value;
158
+ return typeof p.id === "string" && typeof p.name === "string" && typeof p.baseUrl === "string" && typeof p.apiKey === "string";
159
+ }
160
+ function normalizeStoredProvider(provider) {
161
+ return { ...provider, providerType: "openai-compatible", responseFormat: provider.responseFormat ?? "auto", manualCapabilities: provider.manualCapabilities ?? {}, defaultModel: provider.defaultModel || undefined, defaultEditModel: provider.defaultEditModel || undefined, defaultVariationModel: provider.defaultVariationModel || undefined };
162
+ }
163
+ function stableProviderId(seed, existing) {
164
+ const base = (seed.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || `custom-${randomUUID().slice(0, 6)}`).slice(0, 40);
165
+ if (!existing.includes(base))
166
+ return base;
167
+ for (let i = 1; i < 1000; i += 1)
168
+ if (!existing.includes(`${base}-${i}`))
169
+ return `${base}-${i}`;
170
+ return `${base}-${randomUUID().slice(0, 6)}`;
171
+ }
172
+ //# sourceMappingURL=global-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"global-config.js","sourceRoot":"","sources":["../../src/core/global-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { ModelDiscoveryCache } from "./cache.js";
2
+ import { ModelRegistry } from "./model-registry.js";
3
+ import type { DiscoveryResult, EditParams, GenerateParams, ImageGenConfig, ImageOperationResult, ImageProvider, VariationParams } from "./types.js";
4
+ export declare class ImageService {
5
+ private readonly config;
6
+ private readonly provider;
7
+ private readonly cache;
8
+ constructor(config: ImageGenConfig, provider: ImageProvider, cache?: ModelDiscoveryCache);
9
+ discover(refresh?: boolean, signal?: AbortSignal): Promise<DiscoveryResult>;
10
+ registry(refresh?: boolean, signal?: AbortSignal): Promise<ModelRegistry>;
11
+ generate(params: GenerateParams, signal?: AbortSignal): Promise<ImageOperationResult>;
12
+ edit(params: EditParams, signal?: AbortSignal): Promise<ImageOperationResult>;
13
+ variation(params: VariationParams, signal?: AbortSignal): Promise<ImageOperationResult>;
14
+ }
@@ -0,0 +1,42 @@
1
+ import { ModelDiscoveryCache } from "./cache.js";
2
+ import { ModelRegistry } from "./model-registry.js";
3
+ export class ImageService {
4
+ config;
5
+ provider;
6
+ cache;
7
+ constructor(config, provider, cache = new ModelDiscoveryCache()) {
8
+ this.config = config;
9
+ this.provider = provider;
10
+ this.cache = cache;
11
+ }
12
+ async discover(refresh = false, signal) {
13
+ if (!refresh) {
14
+ const cached = await this.cache.get(this.config);
15
+ if (cached)
16
+ return cached;
17
+ }
18
+ const models = await this.provider.discoverModels(signal);
19
+ const result = { providerId: this.config.providerId, providerName: this.config.providerName, providerType: this.provider.type, baseUrl: this.config.baseUrl, models, discoveredAt: new Date().toISOString(), apiKeyFingerprint: this.cache.apiKeyFingerprint(this.config) };
20
+ await this.cache.set(this.config, result);
21
+ return result;
22
+ }
23
+ async registry(refresh = false, signal) {
24
+ return new ModelRegistry((await this.discover(refresh, signal)).models);
25
+ }
26
+ async generate(params, signal) {
27
+ const registry = await this.registry(false, signal);
28
+ const model = registry.select("image_generation", params.model, this.config.model);
29
+ return this.provider.generate({ ...params, model: model.id, modelCapabilities: model }, signal);
30
+ }
31
+ async edit(params, signal) {
32
+ const registry = await this.registry(false, signal);
33
+ const model = registry.select("image_edit", params.model, this.config.editModel ?? this.config.model);
34
+ return this.provider.edit({ ...params, model: model.id, modelCapabilities: model }, signal);
35
+ }
36
+ async variation(params, signal) {
37
+ const registry = await this.registry(false, signal);
38
+ const model = registry.select("image_variation", params.model, this.config.variationModel ?? this.config.model);
39
+ return this.provider.variation({ ...params, model: model.id, modelCapabilities: model }, signal);
40
+ }
41
+ }
42
+ //# sourceMappingURL=image-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-service.js","sourceRoot":"","sources":["../../src/core/image-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { DiscoveryResult, ModelBrowserOptions } from "./types.js";
2
+ export declare const DEFAULT_MODEL_LIMIT = 20;
3
+ export declare const MAX_MODEL_LIMIT = 50;
4
+ export declare function renderModelBrowser(result: DiscoveryResult, options: ModelBrowserOptions): string;
5
+ export declare function renderRefreshSummary(result: DiscoveryResult): string;
@@ -0,0 +1,76 @@
1
+ import { ModelRegistry } from "./model-registry.js";
2
+ export const DEFAULT_MODEL_LIMIT = 20;
3
+ export const MAX_MODEL_LIMIT = 50;
4
+ export function renderModelBrowser(result, options) {
5
+ const limit = Math.min(Math.max(1, options.limit), MAX_MODEL_LIMIT);
6
+ const page = Math.max(1, options.page);
7
+ const registry = new ModelRegistry(result.models);
8
+ const all = registry.all();
9
+ const keyword = options.search?.trim().toLowerCase();
10
+ const filtered = keyword ? all.filter((m) => `${m.id} ${JSON.stringify(m.rawMetadata)}`.toLowerCase().includes(keyword)) : all;
11
+ const groups = [["image_generation", "图片生成"], ["image_edit", "图片编辑"], ["image_variation", "图片变体"], ["vision", "视觉理解"], ["unknown", "未知能力"]];
12
+ if (options.includeText || keyword)
13
+ groups.push(["text", "文本模型"]);
14
+ const lines = [
15
+ `提供商:${result.providerName} (${result.providerId})`,
16
+ `模型总数:${all.length}`,
17
+ keyword ? `搜索:${keyword}` : "默认视图:只显示图片相关模型",
18
+ `显示限制:每组 ${limit} 个,第 ${page} 页`,
19
+ "",
20
+ ];
21
+ for (const [cap, label] of groups) {
22
+ const models = filtered.filter((m) => m.capabilities.includes(cap));
23
+ const start = (page - 1) * limit;
24
+ const shown = models.slice(start, start + limit);
25
+ const totalPages = Math.max(1, Math.ceil(models.length / limit));
26
+ lines.push(`${label}(${models.length})${models.length > limit ? ` - 第 ${Math.min(page, totalPages)} / ${totalPages} 页` : ""}`);
27
+ if (shown.length === 0)
28
+ lines.push("- 无");
29
+ else
30
+ shown.forEach((m, i) => lines.push(`${start + i + 1}. ${formatModel(m)}`));
31
+ if (models.length > start + shown.length)
32
+ lines.push(`... 还有 ${models.length - start - shown.length} 个。请在 /image-config 中使用“搜索模型”或调整分页查看。`);
33
+ lines.push("");
34
+ }
35
+ try {
36
+ lines.push(`当前默认生图模型:${registry.select("image_generation").id}`);
37
+ }
38
+ catch {
39
+ lines.push("当前默认生图模型:无");
40
+ }
41
+ if (all.length > filtered.length)
42
+ lines.push(`已过滤:${all.length - filtered.length}`);
43
+ if (!keyword)
44
+ lines.push("请在 /image-config 中使用“搜索模型”搜索完整模型列表。");
45
+ return truncateLines(lines, 180, 12_000);
46
+ }
47
+ export function renderRefreshSummary(result) {
48
+ const summary = new ModelRegistry(result.models).summary();
49
+ return [
50
+ `提供商已刷新:${result.providerName} (${result.providerId})`,
51
+ `模型总数:${result.models.length}`,
52
+ `图片生成:${summary.image_generation.length}`,
53
+ `图片编辑:${summary.image_edit.length}`,
54
+ `图片变体:${summary.image_variation.length}`,
55
+ `视觉理解:${summary.vision.length}`,
56
+ `未知能力:${summary.unknown.length}`,
57
+ ].join("\n");
58
+ }
59
+ function formatModel(model) {
60
+ const source = model.capabilitySource === "heuristic" ? " [heuristic]" : model.capabilitySource === "manual" ? " [manual]" : "";
61
+ return `${model.id}${source}`;
62
+ }
63
+ function truncateLines(lines, maxLines, maxChars) {
64
+ const out = [];
65
+ let chars = 0;
66
+ for (const line of lines) {
67
+ if (out.length >= maxLines || chars + line.length + 1 > maxChars) {
68
+ out.push("... 输出已截断。请在 /image-config 中使用搜索、分页或选择提供商缩小结果。");
69
+ break;
70
+ }
71
+ out.push(line);
72
+ chars += line.length + 1;
73
+ }
74
+ return out.join("\n");
75
+ }
76
+ //# sourceMappingURL=model-browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-browser.js","sourceRoot":"","sources":["../../src/core/model-browser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { ModelCapability, ModelInfo } from "./types.js";
2
+ export declare class ModelRegistry {
3
+ private readonly models;
4
+ constructor(models: ModelInfo[]);
5
+ all(): ModelInfo[];
6
+ byCapability(cap: ModelCapability): ModelInfo[];
7
+ get(id: string): ModelInfo | undefined;
8
+ select(capability: ModelCapability, explicit?: string, configured?: string): ModelInfo;
9
+ summary(): Record<ModelCapability, string[]>;
10
+ }
@@ -0,0 +1,44 @@
1
+ import { ImageGenError } from "./errors.js";
2
+ export class ModelRegistry {
3
+ models;
4
+ constructor(models) {
5
+ this.models = models;
6
+ }
7
+ all() { return [...this.models].sort((a, b) => a.id.localeCompare(b.id)); }
8
+ byCapability(cap) { return this.all().filter((m) => m.capabilities.includes(cap)); }
9
+ get(id) { return this.models.find((m) => m.id === id); }
10
+ select(capability, explicit, configured) {
11
+ const preferred = explicit || configured;
12
+ if (preferred) {
13
+ const model = this.get(preferred);
14
+ if (!model)
15
+ throw new ImageGenError("model_unavailable", `模型 ${preferred} 不在已发现模型列表中。`, "请在 /image-config 中刷新模型,或确认模型名称。 ");
16
+ if (!model.capabilities.includes(capability) && !model.capabilities.includes("unknown")) {
17
+ throw new ImageGenError("model_unavailable", `模型 ${preferred} 未声明支持 ${capability}。`, "请在 /image-config 中查看可用模型,或用 IMAGE_MODEL_CAPABILITIES 手动声明能力。 ");
18
+ }
19
+ if (model.capabilities.includes("unknown")) {
20
+ throw new ImageGenError("capability_unknown", `无法自动判断模型 ${preferred} 是否支持 ${capability}。`, "请设置 IMAGE_MODEL_CAPABILITIES='model:image_generation' 之类的手动能力声明。 ");
21
+ }
22
+ return model;
23
+ }
24
+ const capable = this.byCapability(capability);
25
+ if (capable.length > 0)
26
+ return capable[0];
27
+ const unknown = this.byCapability("unknown");
28
+ if (unknown.length > 0) {
29
+ throw new ImageGenError("capability_unknown", `没有可确认支持 ${capability} 的模型,但发现 unknown 模型。`, `请在 /image-config 中查看模型,并用 IMAGE_MODEL 或 IMAGE_MODEL_CAPABILITIES 手动指定。`);
30
+ }
31
+ throw new ImageGenError("model_unavailable", `没有发现支持 ${capability} 的模型。`, "检查 API 是否支持图片接口,或手动配置对应模型。 ");
32
+ }
33
+ summary() {
34
+ return {
35
+ image_generation: this.byCapability("image_generation").map((m) => m.id),
36
+ image_edit: this.byCapability("image_edit").map((m) => m.id),
37
+ image_variation: this.byCapability("image_variation").map((m) => m.id),
38
+ vision: this.byCapability("vision").map((m) => m.id),
39
+ text: this.byCapability("text").map((m) => m.id),
40
+ unknown: this.byCapability("unknown").map((m) => m.id),
41
+ };
42
+ }
43
+ }
44
+ //# sourceMappingURL=model-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-registry.js","sourceRoot":"","sources":["../../src/core/model-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { ImageGenConfig, ImageProvider } from "./types.js";
2
+ export declare function resolveProvider(config: ImageGenConfig): ImageProvider;
@@ -0,0 +1,8 @@
1
+ import { ImageGenError } from "./errors.js";
2
+ import { OpenAICompatibleProvider } from "../providers/openai-compatible.js";
3
+ export function resolveProvider(config) {
4
+ if (config.providerType === "openai-compatible")
5
+ return new OpenAICompatibleProvider(config);
6
+ throw new ImageGenError("unsupported_provider", `不支持的 Provider:${config.providerType}`, "当前版本只实现 OpenAI-compatible。 ");
7
+ }
8
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/core/provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAE7E,MAAM,UAAU,eAAe,CAAC,MAAsB;IACpD,IAAI,MAAM,CAAC,YAAY,KAAK,mBAAmB;QAAE,OAAO,IAAI,wBAAwB,CAAC,MAAM,CAAC,CAAC;IAC7F,MAAM,IAAI,aAAa,CAAC,sBAAsB,EAAE,iBAAiB,MAAM,CAAC,YAAY,EAAE,EAAE,6BAA6B,CAAC,CAAC;AACzH,CAAC"}
@@ -0,0 +1,122 @@
1
+ export type ModelCapability = "text" | "vision" | "image_generation" | "image_edit" | "image_variation" | "unknown";
2
+ export interface ModelInfo {
3
+ id: string;
4
+ provider: string;
5
+ capabilities: ModelCapability[];
6
+ capabilitySource: "metadata" | "heuristic" | "manual" | "unknown";
7
+ inputModalities: string[];
8
+ outputModalities: string[];
9
+ supportsSize: boolean;
10
+ supportsQuality: boolean;
11
+ supportsAspectRatio: boolean;
12
+ supportsImageInput: boolean;
13
+ supportsMultipleImages: boolean;
14
+ rawMetadata: unknown;
15
+ }
16
+ export type ImageResponseFormat = "auto" | "url" | "b64_json";
17
+ export interface StoredProviderConfig {
18
+ id: string;
19
+ name: string;
20
+ providerType: "openai-compatible";
21
+ baseUrl: string;
22
+ apiKey: string;
23
+ defaultModel?: string | undefined;
24
+ defaultEditModel?: string | undefined;
25
+ defaultVariationModel?: string | undefined;
26
+ /** auto: 优先 url,服务端明确拒绝时仅重试一次 b64_json。 */
27
+ responseFormat?: ImageResponseFormat | undefined;
28
+ manualCapabilities: Record<string, ModelCapability[]>;
29
+ createdAt: string;
30
+ updatedAt: string;
31
+ apiKeyUpdatedAt: string;
32
+ }
33
+ export interface GlobalImageGenConfig {
34
+ version: 1;
35
+ defaultProviderId?: string | undefined;
36
+ providers: StoredProviderConfig[];
37
+ }
38
+ export interface ImageGenConfig {
39
+ providerId: string;
40
+ providerName: string;
41
+ baseUrl: string;
42
+ apiKey: string;
43
+ model?: string | undefined;
44
+ editModel?: string | undefined;
45
+ variationModel?: string | undefined;
46
+ responseFormat: ImageResponseFormat;
47
+ providerType: "openai-compatible";
48
+ timeoutMs: number;
49
+ cacheTtlMs: number;
50
+ outputDir: string;
51
+ maxResponseBytes: number;
52
+ maxDownloadBytes: number;
53
+ manualCapabilities: Record<string, ModelCapability[]>;
54
+ apiKeyUpdatedAt: string;
55
+ }
56
+ export interface DiscoveryResult {
57
+ providerId: string;
58
+ providerName: string;
59
+ providerType: string;
60
+ baseUrl: string;
61
+ models: ModelInfo[];
62
+ discoveredAt: string;
63
+ apiKeyFingerprint: string;
64
+ }
65
+ export interface GeneratedImage {
66
+ path: string;
67
+ url?: string;
68
+ mimeType: string;
69
+ index: number;
70
+ }
71
+ export interface ImageOperationResult {
72
+ provider: string;
73
+ providerId: string;
74
+ model: string;
75
+ images: GeneratedImage[];
76
+ rawInfo?: Record<string, unknown>;
77
+ }
78
+ export interface GenerateParams {
79
+ prompt: string;
80
+ provider?: string;
81
+ model?: string;
82
+ size?: string;
83
+ aspect_ratio?: string;
84
+ quality?: string;
85
+ output_format?: "png" | "jpeg" | "jpg" | "webp";
86
+ n?: number;
87
+ modelCapabilities?: Pick<ModelInfo, "supportsSize" | "supportsQuality" | "supportsAspectRatio" | "supportsMultipleImages">;
88
+ }
89
+ export interface EditParams {
90
+ image: string;
91
+ prompt: string;
92
+ provider?: string;
93
+ model?: string;
94
+ size?: string;
95
+ output_format?: "png" | "jpeg" | "jpg" | "webp";
96
+ modelCapabilities?: Pick<ModelInfo, "supportsSize">;
97
+ }
98
+ export interface VariationParams {
99
+ image: string;
100
+ provider?: string;
101
+ model?: string;
102
+ size?: string;
103
+ n?: number;
104
+ modelCapabilities?: Pick<ModelInfo, "supportsSize" | "supportsMultipleImages">;
105
+ }
106
+ export interface ModelBrowserOptions {
107
+ provider?: string;
108
+ search?: string;
109
+ page: number;
110
+ limit: number;
111
+ refresh: boolean;
112
+ refreshAll: boolean;
113
+ includeText: boolean;
114
+ }
115
+ export interface ImageProvider {
116
+ readonly type: string;
117
+ readonly name: string;
118
+ discoverModels(signal?: AbortSignal): Promise<ModelInfo[]>;
119
+ generate(params: GenerateParams, signal?: AbortSignal): Promise<ImageOperationResult>;
120
+ edit(params: EditParams, signal?: AbortSignal): Promise<ImageOperationResult>;
121
+ variation(params: VariationParams, signal?: AbortSignal): Promise<ImageOperationResult>;
122
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ export default function imageGenExtension(pi: ExtensionAPI): void;