@neta-art/generation 0.1.1 → 0.1.2

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.
package/README.md CHANGED
@@ -60,6 +60,42 @@ pnpm example:image-editing
60
60
  pnpm example:text-to-video
61
61
  ```
62
62
 
63
+ You can also call providers through the CLI:
64
+
65
+ ```bash
66
+ node --env-file=.env ./dist/cli/index.js generate gemini-3.1-flash-image-preview \
67
+ --prompt "a simple abstract geometric app icon" \
68
+ --param aspect_ratio=1:1 \
69
+ --param image_size=512 \
70
+ --debug
71
+ ```
72
+
73
+ Use `--image-url` for reference images, `--out` to write base64 outputs to files, and `json:` for non-string parameter values, for example `--param duration=json:5`.
74
+
75
+ ## Debug provider requests
76
+
77
+ Pass `debug: true` to print the final provider request and response metadata to stderr. Sensitive fields such as `Authorization` and base64 image data are redacted by default.
78
+
79
+ ```ts
80
+ const client = createGenerationClient({
81
+ apiKey: process.env.NETA_ROUTER_API_KEY!,
82
+ debug: true,
83
+ });
84
+ ```
85
+
86
+ For a custom logger or full unredacted payloads:
87
+
88
+ ```ts
89
+ const client = createGenerationClient({
90
+ apiKey: process.env.NETA_ROUTER_API_KEY!,
91
+ debug: {
92
+ enabled: true,
93
+ includeSensitive: true,
94
+ logger: (event) => console.error(JSON.stringify(event, null, 2)),
95
+ },
96
+ });
97
+ ```
98
+
63
99
  ## Built-in models
64
100
 
65
101
  - `gpt-image-2`
@@ -1 +1 @@
1
- {"version":3,"file":"builtins-B1AheaEa.js","names":["builtinGenerationModels: GenerationModelDeclaration[]"],"sources":["../src/types.ts","../src/utils.ts","../src/builtins.ts"],"sourcesContent":["export const MODEL_SCHEMA = \"neta.generation.model.v1\" as const;\n\nexport type GenerationSource = { type: \"url\"; url: string } | { type: \"base64\"; mediaType: string; data: string };\n\nexport type GenerationContentBlockMeta = Record<string, unknown>;\n\nexport type GenerationContentBlock =\n | { type: \"text\"; text: string; meta?: GenerationContentBlockMeta }\n | { type: \"image\"; source: GenerationSource; meta?: GenerationContentBlockMeta }\n | { type: \"video\"; source: GenerationSource; meta?: GenerationContentBlockMeta }\n | { type: \"audio\"; source: GenerationSource; meta?: GenerationContentBlockMeta };\n\nexport type GenerationContentSpec = {\n type: \"text\" | \"image\" | \"video\" | \"audio\";\n required?: boolean;\n min?: number;\n max?: number;\n sources?: Array<GenerationSource[\"type\"]>;\n merge?: \"newline\" | \"space\" | \"concat\";\n meta?: Record<string, unknown>;\n description?: string;\n};\n\nexport type GenerationParameterSpec =\n | {\n type: \"string\";\n optional?: boolean;\n default?: string;\n enum?: string[];\n description?: string;\n examples?: string[];\n }\n | {\n type: \"number\";\n optional?: boolean;\n default?: number;\n min?: number;\n max?: number;\n description?: string;\n examples?: number[];\n }\n | {\n type: \"integer\";\n optional?: boolean;\n default?: number;\n min?: number;\n max?: number;\n description?: string;\n examples?: number[];\n }\n | {\n type: \"boolean\";\n optional?: boolean;\n default?: boolean;\n description?: string;\n examples?: boolean[];\n };\n\nexport type GenerationModelDeclaration = {\n schema: typeof MODEL_SCHEMA;\n model: string;\n title?: string;\n description?: string;\n adapter: {\n type: string;\n };\n content: {\n input: GenerationContentSpec[];\n };\n parameters?: Record<string, GenerationParameterSpec>;\n examples?: Array<{\n title?: string;\n request: GenerateRequest;\n }>;\n};\n\nexport type GenerateRequest = {\n model: string;\n content: GenerationContentBlock[];\n parameters?: Record<string, unknown>;\n apiKey?: string;\n baseUrl?: string;\n metadata?: Record<string, unknown>;\n};\n\nexport type ResolvedGenerationRequest = {\n declaration: GenerationModelDeclaration;\n request: GenerateRequest;\n parameters: Record<string, unknown>;\n};\n\nexport type GenerationSourceResolver = (source: GenerationSource) => Promise<string> | string;\n\nexport type GenerationAdapterContext = {\n apiKey: string;\n baseUrl: string;\n fetch: typeof fetch;\n resolveSource: GenerationSourceResolver;\n};\n\nexport type GenerationAdapterInput = ResolvedGenerationRequest & {\n context: GenerationAdapterContext;\n};\n\nexport type GenerationAdapter = (input: GenerationAdapterInput) => Promise<GenerationContentBlock[]>;\n\nexport type CreateGenerationClientOptions = {\n apiKey?: string;\n baseUrl?: string;\n models?: GenerationModelDeclaration[];\n includeBuiltinModels?: boolean;\n fetch?: typeof fetch;\n sourceResolver?: GenerationSourceResolver;\n adapters?: Record<string, GenerationAdapter>;\n};\n\nexport type GenerationClient = {\n generate(request: GenerateRequest): Promise<GenerationContentBlock[]>;\n validate(request: GenerateRequest): ResolvedGenerationRequest;\n listModels(): GenerationModelDeclaration[];\n getModel(model: string): GenerationModelDeclaration | null;\n stringifyModelConfig(model: string, options?: { format?: \"yaml\" | \"json\" }): string;\n exportModelConfig(model: string, filePath: string): Promise<void>;\n exportModelConfigs(directory: string): Promise<void>;\n};\n","import type { GenerationContentBlock } from \"./types.js\";\n\nexport function cloneJson<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\nexport function slugifyFileName(value: string): string {\n return (\n value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\") || \"model\"\n );\n}\n\nexport function getBlockMeta(block: GenerationContentBlock): Record<string, unknown> | undefined {\n return \"meta\" in block ? block.meta : undefined;\n}\n\nexport function compactArray<T>(values: T[]): T[] | undefined {\n return values.length > 0 ? values : undefined;\n}\n\nexport function compactObject<T extends Record<string, unknown>>(value: T): T {\n for (const key of Object.keys(value)) if (value[key] === undefined) delete value[key];\n return value;\n}\n","import type { GenerationModelDeclaration } from \"./types.js\";\nimport { MODEL_SCHEMA } from \"./types.js\";\nimport { cloneJson } from \"./utils.js\";\n\nconst imageSizeParameters = {\n size: {\n type: \"string\",\n optional: true,\n default: \"1024x1024\",\n description: \"Output image size.\",\n examples: [\"auto\", \"1024x1024\", \"1536x1024\", \"1024x1536\", \"2048x2048\", \"2048x1152\", \"3840x2160\", \"2160x3840\"],\n },\n quality: {\n type: \"string\",\n optional: true,\n default: \"auto\",\n enum: [\"auto\", \"low\", \"medium\", \"high\"],\n description: \"Image quality.\",\n },\n} satisfies GenerationModelDeclaration[\"parameters\"];\n\nfunction videoParameters(defaults: { resolution: string; maxWait: number }) {\n return {\n duration: {\n type: \"integer\",\n optional: true,\n default: 5,\n min: 4,\n max: 15,\n description: \"Video duration in seconds.\",\n },\n resolution: {\n type: \"string\",\n optional: true,\n default: defaults.resolution,\n enum: [\"480p\", \"720p\", \"1080p\", \"2K\"],\n description: \"Output video resolution.\",\n },\n aspect_ratio: {\n type: \"string\",\n optional: true,\n default: \"16:9\",\n enum: [\"16:9\", \"9:16\", \"1:1\", \"4:3\", \"3:2\", \"2:3\", \"3:4\", \"21:9\", \"adaptive\"],\n description: \"Output aspect ratio. Use adaptive to let the model choose.\",\n },\n fps: { type: \"integer\", optional: true, default: 30, min: 1, max: 60, description: \"Frames per second.\" },\n seed: { type: \"integer\", optional: true, description: \"Random seed for reproducibility.\" },\n generate_audio: { type: \"boolean\", optional: true, default: true, description: \"Generate synchronized audio.\" },\n return_last_frame: {\n type: \"boolean\",\n optional: true,\n default: true,\n description: \"Return the last frame as an image for chaining video segments.\",\n },\n camera_fixed: {\n type: \"boolean\",\n optional: true,\n default: false,\n description: \"Fix camera position when supported.\",\n },\n watermark: { type: \"boolean\", optional: true, default: false, description: \"Add AI Generated watermark.\" },\n poll_interval: {\n type: \"integer\",\n optional: true,\n default: 2,\n min: 1,\n max: 30,\n description: \"Seconds between task status checks.\",\n },\n max_wait: {\n type: \"integer\",\n optional: true,\n default: defaults.maxWait,\n min: 30,\n max: 1800,\n description: \"Maximum seconds to wait for task completion.\",\n },\n } satisfies GenerationModelDeclaration[\"parameters\"];\n}\n\nconst builtinModels = [\n {\n schema: MODEL_SCHEMA,\n model: \"gpt-image-2\",\n title: \"GPT Image 2\",\n description:\n \"Image generation model with optional reference images. Good for photorealistic scenes, detailed images, and image editing with references.\",\n adapter: { type: \"openai.images\" },\n content: {\n input: [\n { type: \"text\", required: true, min: 1, max: 16, merge: \"newline\", description: \"Prompt text.\" },\n {\n type: \"image\",\n required: false,\n max: 16,\n sources: [\"url\", \"base64\"],\n description: \"Optional reference images.\",\n },\n ],\n },\n parameters: imageSizeParameters,\n examples: [\n {\n title: \"Basic image\",\n request: {\n model: \"gpt-image-2\",\n content: [{ type: \"text\", text: \"a cyberpunk cat in neon rain\" }],\n parameters: { size: \"1024x1024\", quality: \"auto\" },\n },\n },\n ],\n },\n {\n schema: MODEL_SCHEMA,\n model: \"gemini-3.1-flash-image-preview\",\n title: \"Gemini 3.1 Flash Image Preview\",\n description:\n \"Gemini image generation and editing model. Good for text rendering, infographics, style transfer, and iterative image editing with references.\",\n adapter: { type: \"gemini.generateContent\" },\n content: {\n input: [\n { type: \"text\", required: true, min: 1, max: 16, merge: \"newline\", description: \"Prompt text.\" },\n {\n type: \"image\",\n required: false,\n max: 14,\n sources: [\"url\", \"base64\"],\n description: \"Optional reference images.\",\n },\n ],\n },\n parameters: {\n aspect_ratio: {\n type: \"string\",\n optional: true,\n default: \"1:1\",\n enum: [\"1:1\", \"16:9\", \"4:3\", \"3:2\", \"3:4\", \"2:3\", \"9:16\", \"5:4\", \"4:5\", \"21:9\", \"1:4\", \"4:1\", \"1:8\", \"8:1\"],\n description: \"Output aspect ratio.\",\n },\n image_size: {\n type: \"string\",\n optional: true,\n default: \"2K\",\n enum: [\"512\", \"1K\", \"2K\", \"4K\"],\n description: \"Output image resolution.\",\n },\n },\n examples: [\n {\n title: \"Basic image\",\n request: {\n model: \"gemini-3.1-flash-image-preview\",\n content: [\n { type: \"text\", text: \"a vibrant infographic explaining photosynthesis with clear readable labels\" },\n ],\n parameters: { aspect_ratio: \"16:9\", image_size: \"1K\" },\n },\n },\n ],\n },\n {\n schema: MODEL_SCHEMA,\n model: \"seedance-2-0\",\n title: \"Seedance 2.0\",\n description: \"Higher quality Ark video generation model for final production outputs.\",\n adapter: { type: \"ark.videoGenerations\" },\n content: {\n input: [\n { type: \"text\", required: true, min: 1, max: 16, merge: \"newline\", description: \"Video prompt.\" },\n {\n type: \"image\",\n required: false,\n max: 9,\n sources: [\"url\", \"base64\"],\n description: \"Optional image input. Use meta.role as first_frame, last_frame, or reference_image.\",\n },\n ],\n },\n parameters: videoParameters({ resolution: \"1080p\", maxWait: 900 }),\n },\n {\n schema: MODEL_SCHEMA,\n model: \"seedance-2-0-fast\",\n title: \"Seedance 2.0 Fast\",\n description:\n \"Fast Ark video generation model for drafts, rapid iteration, text-to-video, image-to-video, and reference-guided video generation.\",\n adapter: { type: \"ark.videoGenerations\" },\n content: {\n input: [\n { type: \"text\", required: true, min: 1, max: 16, merge: \"newline\", description: \"Video prompt.\" },\n {\n type: \"image\",\n required: false,\n max: 9,\n sources: [\"url\", \"base64\"],\n description: \"Optional image input. Use meta.role as first_frame, last_frame, or reference_image.\",\n },\n ],\n },\n parameters: videoParameters({ resolution: \"720p\", maxWait: 600 }),\n },\n] satisfies GenerationModelDeclaration[];\n\nexport const builtinGenerationModels: GenerationModelDeclaration[] = cloneJson(builtinModels);\n\nexport function getBuiltinGenerationModel(model: string): GenerationModelDeclaration | null {\n return cloneJson(builtinModels.find((declaration) => declaration.model === model) ?? null);\n}\n\nexport function listBuiltinGenerationModels(): GenerationModelDeclaration[] {\n return cloneJson(builtinModels);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"builtins-B1AheaEa.js","names":["builtinGenerationModels: GenerationModelDeclaration[]"],"sources":["../src/types.ts","../src/utils.ts","../src/builtins.ts"],"sourcesContent":["export const MODEL_SCHEMA = \"neta.generation.model.v1\" as const;\n\nexport type GenerationSource = { type: \"url\"; url: string } | { type: \"base64\"; mediaType: string; data: string };\n\nexport type GenerationContentBlockMeta = Record<string, unknown>;\n\nexport type GenerationContentBlock =\n | { type: \"text\"; text: string; meta?: GenerationContentBlockMeta }\n | { type: \"image\"; source: GenerationSource; meta?: GenerationContentBlockMeta }\n | { type: \"video\"; source: GenerationSource; meta?: GenerationContentBlockMeta }\n | { type: \"audio\"; source: GenerationSource; meta?: GenerationContentBlockMeta };\n\nexport type GenerationContentSpec = {\n type: \"text\" | \"image\" | \"video\" | \"audio\";\n required?: boolean;\n min?: number;\n max?: number;\n sources?: Array<GenerationSource[\"type\"]>;\n merge?: \"newline\" | \"space\" | \"concat\";\n meta?: Record<string, unknown>;\n description?: string;\n};\n\nexport type GenerationParameterSpec =\n | {\n type: \"string\";\n optional?: boolean;\n default?: string;\n enum?: string[];\n description?: string;\n examples?: string[];\n }\n | {\n type: \"number\";\n optional?: boolean;\n default?: number;\n min?: number;\n max?: number;\n description?: string;\n examples?: number[];\n }\n | {\n type: \"integer\";\n optional?: boolean;\n default?: number;\n min?: number;\n max?: number;\n description?: string;\n examples?: number[];\n }\n | {\n type: \"boolean\";\n optional?: boolean;\n default?: boolean;\n description?: string;\n examples?: boolean[];\n };\n\nexport type GenerationModelDeclaration = {\n schema: typeof MODEL_SCHEMA;\n model: string;\n title?: string;\n description?: string;\n adapter: {\n type: string;\n };\n content: {\n input: GenerationContentSpec[];\n };\n parameters?: Record<string, GenerationParameterSpec>;\n examples?: Array<{\n title?: string;\n request: GenerateRequest;\n }>;\n};\n\nexport type GenerateRequest = {\n model: string;\n content: GenerationContentBlock[];\n parameters?: Record<string, unknown>;\n apiKey?: string;\n baseUrl?: string;\n metadata?: Record<string, unknown>;\n};\n\nexport type ResolvedGenerationRequest = {\n declaration: GenerationModelDeclaration;\n request: GenerateRequest;\n parameters: Record<string, unknown>;\n};\n\nexport type GenerationSourceResolver = (source: GenerationSource) => Promise<string> | string;\n\nexport type GenerationDebugEvent =\n | {\n type: \"request\";\n url: string;\n method: string;\n headers: Record<string, string>;\n body?: unknown;\n }\n | {\n type: \"response\";\n url: string;\n status: number;\n statusText: string;\n headers: Record<string, string>;\n trace: Record<string, string>;\n elapsedMs: number;\n body?: unknown;\n };\n\nexport type GenerationDebugLogger = (event: GenerationDebugEvent) => void;\n\nexport type GenerationDebugOptions = {\n enabled?: boolean;\n includeSensitive?: boolean;\n includeResponseBody?: boolean;\n logger?: GenerationDebugLogger;\n};\n\nexport type GenerationDebugConfig = GenerationDebugOptions & {\n enabled: boolean;\n includeSensitive: boolean;\n includeResponseBody: boolean;\n logger: GenerationDebugLogger;\n};\n\nexport type GenerationAdapterContext = {\n apiKey: string;\n baseUrl: string;\n fetch: typeof fetch;\n resolveSource: GenerationSourceResolver;\n};\n\nexport type GenerationAdapterInput = ResolvedGenerationRequest & {\n context: GenerationAdapterContext;\n};\n\nexport type GenerationAdapter = (input: GenerationAdapterInput) => Promise<GenerationContentBlock[]>;\n\nexport type CreateGenerationClientOptions = {\n apiKey?: string;\n baseUrl?: string;\n models?: GenerationModelDeclaration[];\n includeBuiltinModels?: boolean;\n fetch?: typeof fetch;\n sourceResolver?: GenerationSourceResolver;\n adapters?: Record<string, GenerationAdapter>;\n debug?: boolean | GenerationDebugOptions;\n};\n\nexport type GenerationClient = {\n generate(request: GenerateRequest): Promise<GenerationContentBlock[]>;\n validate(request: GenerateRequest): ResolvedGenerationRequest;\n listModels(): GenerationModelDeclaration[];\n getModel(model: string): GenerationModelDeclaration | null;\n stringifyModelConfig(model: string, options?: { format?: \"yaml\" | \"json\" }): string;\n exportModelConfig(model: string, filePath: string): Promise<void>;\n exportModelConfigs(directory: string): Promise<void>;\n};\n","import type { GenerationContentBlock } from \"./types.js\";\n\nexport function cloneJson<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\nexport function slugifyFileName(value: string): string {\n return (\n value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\") || \"model\"\n );\n}\n\nexport function getBlockMeta(block: GenerationContentBlock): Record<string, unknown> | undefined {\n return \"meta\" in block ? block.meta : undefined;\n}\n\nexport function compactArray<T>(values: T[]): T[] | undefined {\n return values.length > 0 ? values : undefined;\n}\n\nexport function compactObject<T extends Record<string, unknown>>(value: T): T {\n for (const key of Object.keys(value)) if (value[key] === undefined) delete value[key];\n return value;\n}\n","import type { GenerationModelDeclaration } from \"./types.js\";\nimport { MODEL_SCHEMA } from \"./types.js\";\nimport { cloneJson } from \"./utils.js\";\n\nconst imageSizeParameters = {\n size: {\n type: \"string\",\n optional: true,\n default: \"1024x1024\",\n description: \"Output image size.\",\n examples: [\"auto\", \"1024x1024\", \"1536x1024\", \"1024x1536\", \"2048x2048\", \"2048x1152\", \"3840x2160\", \"2160x3840\"],\n },\n quality: {\n type: \"string\",\n optional: true,\n default: \"auto\",\n enum: [\"auto\", \"low\", \"medium\", \"high\"],\n description: \"Image quality.\",\n },\n} satisfies GenerationModelDeclaration[\"parameters\"];\n\nfunction videoParameters(defaults: { resolution: string; maxWait: number }) {\n return {\n duration: {\n type: \"integer\",\n optional: true,\n default: 5,\n min: 4,\n max: 15,\n description: \"Video duration in seconds.\",\n },\n resolution: {\n type: \"string\",\n optional: true,\n default: defaults.resolution,\n enum: [\"480p\", \"720p\", \"1080p\", \"2K\"],\n description: \"Output video resolution.\",\n },\n aspect_ratio: {\n type: \"string\",\n optional: true,\n default: \"16:9\",\n enum: [\"16:9\", \"9:16\", \"1:1\", \"4:3\", \"3:2\", \"2:3\", \"3:4\", \"21:9\", \"adaptive\"],\n description: \"Output aspect ratio. Use adaptive to let the model choose.\",\n },\n fps: { type: \"integer\", optional: true, default: 30, min: 1, max: 60, description: \"Frames per second.\" },\n seed: { type: \"integer\", optional: true, description: \"Random seed for reproducibility.\" },\n generate_audio: { type: \"boolean\", optional: true, default: true, description: \"Generate synchronized audio.\" },\n return_last_frame: {\n type: \"boolean\",\n optional: true,\n default: true,\n description: \"Return the last frame as an image for chaining video segments.\",\n },\n camera_fixed: {\n type: \"boolean\",\n optional: true,\n default: false,\n description: \"Fix camera position when supported.\",\n },\n watermark: { type: \"boolean\", optional: true, default: false, description: \"Add AI Generated watermark.\" },\n poll_interval: {\n type: \"integer\",\n optional: true,\n default: 2,\n min: 1,\n max: 30,\n description: \"Seconds between task status checks.\",\n },\n max_wait: {\n type: \"integer\",\n optional: true,\n default: defaults.maxWait,\n min: 30,\n max: 1800,\n description: \"Maximum seconds to wait for task completion.\",\n },\n } satisfies GenerationModelDeclaration[\"parameters\"];\n}\n\nconst builtinModels = [\n {\n schema: MODEL_SCHEMA,\n model: \"gpt-image-2\",\n title: \"GPT Image 2\",\n description:\n \"Image generation model with optional reference images. Good for photorealistic scenes, detailed images, and image editing with references.\",\n adapter: { type: \"openai.images\" },\n content: {\n input: [\n { type: \"text\", required: true, min: 1, max: 16, merge: \"newline\", description: \"Prompt text.\" },\n {\n type: \"image\",\n required: false,\n max: 16,\n sources: [\"url\", \"base64\"],\n description: \"Optional reference images.\",\n },\n ],\n },\n parameters: imageSizeParameters,\n examples: [\n {\n title: \"Basic image\",\n request: {\n model: \"gpt-image-2\",\n content: [{ type: \"text\", text: \"a cyberpunk cat in neon rain\" }],\n parameters: { size: \"1024x1024\", quality: \"auto\" },\n },\n },\n ],\n },\n {\n schema: MODEL_SCHEMA,\n model: \"gemini-3.1-flash-image-preview\",\n title: \"Gemini 3.1 Flash Image Preview\",\n description:\n \"Gemini image generation and editing model. Good for text rendering, infographics, style transfer, and iterative image editing with references.\",\n adapter: { type: \"gemini.generateContent\" },\n content: {\n input: [\n { type: \"text\", required: true, min: 1, max: 16, merge: \"newline\", description: \"Prompt text.\" },\n {\n type: \"image\",\n required: false,\n max: 14,\n sources: [\"url\", \"base64\"],\n description: \"Optional reference images.\",\n },\n ],\n },\n parameters: {\n aspect_ratio: {\n type: \"string\",\n optional: true,\n default: \"1:1\",\n enum: [\"1:1\", \"16:9\", \"4:3\", \"3:2\", \"3:4\", \"2:3\", \"9:16\", \"5:4\", \"4:5\", \"21:9\", \"1:4\", \"4:1\", \"1:8\", \"8:1\"],\n description: \"Output aspect ratio.\",\n },\n image_size: {\n type: \"string\",\n optional: true,\n default: \"2K\",\n enum: [\"512\", \"1K\", \"2K\", \"4K\"],\n description: \"Output image resolution.\",\n },\n },\n examples: [\n {\n title: \"Basic image\",\n request: {\n model: \"gemini-3.1-flash-image-preview\",\n content: [\n { type: \"text\", text: \"a vibrant infographic explaining photosynthesis with clear readable labels\" },\n ],\n parameters: { aspect_ratio: \"16:9\", image_size: \"1K\" },\n },\n },\n ],\n },\n {\n schema: MODEL_SCHEMA,\n model: \"seedance-2-0\",\n title: \"Seedance 2.0\",\n description: \"Higher quality Ark video generation model for final production outputs.\",\n adapter: { type: \"ark.videoGenerations\" },\n content: {\n input: [\n { type: \"text\", required: true, min: 1, max: 16, merge: \"newline\", description: \"Video prompt.\" },\n {\n type: \"image\",\n required: false,\n max: 9,\n sources: [\"url\", \"base64\"],\n description: \"Optional image input. Use meta.role as first_frame, last_frame, or reference_image.\",\n },\n ],\n },\n parameters: videoParameters({ resolution: \"1080p\", maxWait: 900 }),\n },\n {\n schema: MODEL_SCHEMA,\n model: \"seedance-2-0-fast\",\n title: \"Seedance 2.0 Fast\",\n description:\n \"Fast Ark video generation model for drafts, rapid iteration, text-to-video, image-to-video, and reference-guided video generation.\",\n adapter: { type: \"ark.videoGenerations\" },\n content: {\n input: [\n { type: \"text\", required: true, min: 1, max: 16, merge: \"newline\", description: \"Video prompt.\" },\n {\n type: \"image\",\n required: false,\n max: 9,\n sources: [\"url\", \"base64\"],\n description: \"Optional image input. Use meta.role as first_frame, last_frame, or reference_image.\",\n },\n ],\n },\n parameters: videoParameters({ resolution: \"720p\", maxWait: 600 }),\n },\n] satisfies GenerationModelDeclaration[];\n\nexport const builtinGenerationModels: GenerationModelDeclaration[] = cloneJson(builtinModels);\n\nexport function getBuiltinGenerationModel(model: string): GenerationModelDeclaration | null {\n return cloneJson(builtinModels.find((declaration) => declaration.model === model) ?? null);\n}\n\nexport function listBuiltinGenerationModels(): GenerationModelDeclaration[] {\n return cloneJson(builtinModels);\n}\n"],"mappings":"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"}
@@ -97,6 +97,35 @@ type ResolvedGenerationRequest = {
97
97
  parameters: Record<string, unknown>;
98
98
  };
99
99
  type GenerationSourceResolver = (source: GenerationSource) => Promise<string> | string;
100
+ type GenerationDebugEvent = {
101
+ type: "request";
102
+ url: string;
103
+ method: string;
104
+ headers: Record<string, string>;
105
+ body?: unknown;
106
+ } | {
107
+ type: "response";
108
+ url: string;
109
+ status: number;
110
+ statusText: string;
111
+ headers: Record<string, string>;
112
+ trace: Record<string, string>;
113
+ elapsedMs: number;
114
+ body?: unknown;
115
+ };
116
+ type GenerationDebugLogger = (event: GenerationDebugEvent) => void;
117
+ type GenerationDebugOptions = {
118
+ enabled?: boolean;
119
+ includeSensitive?: boolean;
120
+ includeResponseBody?: boolean;
121
+ logger?: GenerationDebugLogger;
122
+ };
123
+ type GenerationDebugConfig = GenerationDebugOptions & {
124
+ enabled: boolean;
125
+ includeSensitive: boolean;
126
+ includeResponseBody: boolean;
127
+ logger: GenerationDebugLogger;
128
+ };
100
129
  type GenerationAdapterContext = {
101
130
  apiKey: string;
102
131
  baseUrl: string;
@@ -115,6 +144,7 @@ type CreateGenerationClientOptions = {
115
144
  fetch?: typeof fetch;
116
145
  sourceResolver?: GenerationSourceResolver;
117
146
  adapters?: Record<string, GenerationAdapter>;
147
+ debug?: boolean | GenerationDebugOptions;
118
148
  };
119
149
  type GenerationClient = {
120
150
  generate(request: GenerateRequest): Promise<GenerationContentBlock[]>;
@@ -133,5 +163,5 @@ declare const builtinGenerationModels: GenerationModelDeclaration[];
133
163
  declare function getBuiltinGenerationModel(model: string): GenerationModelDeclaration | null;
134
164
  declare function listBuiltinGenerationModels(): GenerationModelDeclaration[];
135
165
  //#endregion
136
- export { MODEL_SCHEMA as _, GenerateRequest as a, GenerationAdapterInput as c, GenerationContentBlockMeta as d, GenerationContentSpec as f, GenerationSourceResolver as g, GenerationSource as h, CreateGenerationClientOptions as i, GenerationClient as l, GenerationParameterSpec as m, getBuiltinGenerationModel as n, GenerationAdapter as o, GenerationModelDeclaration as p, listBuiltinGenerationModels as r, GenerationAdapterContext as s, builtinGenerationModels as t, GenerationContentBlock as u, ResolvedGenerationRequest as v };
137
- //# sourceMappingURL=builtins-BuI-rf8X.d.ts.map
166
+ export { ResolvedGenerationRequest as S, GenerationModelDeclaration as _, GenerateRequest as a, GenerationSourceResolver as b, GenerationAdapterInput as c, GenerationContentBlockMeta as d, GenerationContentSpec as f, GenerationDebugOptions as g, GenerationDebugLogger as h, CreateGenerationClientOptions as i, GenerationClient as l, GenerationDebugEvent as m, getBuiltinGenerationModel as n, GenerationAdapter as o, GenerationDebugConfig as p, listBuiltinGenerationModels as r, GenerationAdapterContext as s, builtinGenerationModels as t, GenerationContentBlock as u, GenerationParameterSpec as v, MODEL_SCHEMA as x, GenerationSource as y };
167
+ //# sourceMappingURL=builtins-BE6FhnwP.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builtins-BE6FhnwP.d.ts","names":[],"sources":["../src/types.ts","../src/builtins.ts"],"sourcesContent":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { n as getBuiltinGenerationModel, r as listBuiltinGenerationModels, t as builtinGenerationModels } from "./builtins-BuI-rf8X.js";
1
+ import { n as getBuiltinGenerationModel, r as listBuiltinGenerationModels, t as builtinGenerationModels } from "./builtins-BE6FhnwP.js";
2
2
  export { builtinGenerationModels, getBuiltinGenerationModel, listBuiltinGenerationModels };
package/dist/cli/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { r as listBuiltinGenerationModels } from "../builtins-B1AheaEa.js";
3
- import { n as exportBuiltinModelConfigs, t as exportBuiltinModelConfig, u as stringifyGenerationModelDeclaration } from "../export-config-Bwqnn7Vs.js";
3
+ import { h as stringifyGenerationModelDeclaration, i as createGenerationClient, n as exportBuiltinModelConfigs, t as exportBuiltinModelConfig } from "../export-config-BPnZYpmN.js";
4
4
  import { mkdir, writeFile } from "node:fs/promises";
5
5
  import { dirname, join } from "node:path";
6
6
 
@@ -9,9 +9,13 @@ function usage() {
9
9
  console.log(`neta-generation
10
10
 
11
11
  Usage:
12
+ neta-generation generate <model> --prompt <text> [--param key=value] [--image-url <url>] [--out <directory>] [--debug] [--debug-sensitive] [--no-debug-response-body]
12
13
  neta-generation models list
13
14
  neta-generation models export <model> --out <file>
14
15
  neta-generation models export-all --out <directory>
16
+
17
+ Environment:
18
+ NETA_ROUTER_API_KEY API key used by generate
15
19
  `);
16
20
  process.exit(1);
17
21
  }
@@ -20,8 +24,86 @@ function readOption(args, name) {
20
24
  if (index < 0) return null;
21
25
  return args[index + 1] ?? null;
22
26
  }
27
+ function readOptions(args, name) {
28
+ const values = [];
29
+ for (let index = 0; index < args.length; index += 1) {
30
+ const value = args[index + 1];
31
+ if (args[index] === name && value) values.push(value);
32
+ }
33
+ return values;
34
+ }
35
+ function hasFlag(args, name) {
36
+ return args.includes(name);
37
+ }
38
+ function parseParameterValue(raw) {
39
+ if (raw.startsWith("json:")) return JSON.parse(raw.slice(5));
40
+ if (raw === "true") return true;
41
+ if (raw === "false") return false;
42
+ return raw;
43
+ }
44
+ function parseParameter(value) {
45
+ const separator = value.indexOf("=");
46
+ if (separator <= 0) throw new Error(`Invalid --param value: ${value}`);
47
+ return [value.slice(0, separator), parseParameterValue(value.slice(separator + 1))];
48
+ }
49
+ function outputSummary(block) {
50
+ if (block.type === "text") return block;
51
+ return {
52
+ type: block.type,
53
+ source: block.source.type === "url" ? block.source : {
54
+ type: "base64",
55
+ mediaType: block.source.mediaType,
56
+ bytes: Buffer.byteLength(block.source.data, "base64")
57
+ },
58
+ meta: block.meta
59
+ };
60
+ }
61
+ async function writeOutputFiles(directory, output) {
62
+ await mkdir(directory, { recursive: true });
63
+ await Promise.all(output.map(async (block, index) => {
64
+ if (block.type !== "image" && block.type !== "video" && block.type !== "audio" || block.source.type !== "base64") return;
65
+ const extension = block.source.mediaType.split("/")[1]?.split("+")[0] || block.type;
66
+ await writeFile(join(directory, `${String(index + 1).padStart(2, "0")}.${extension}`), Buffer.from(block.source.data, "base64"));
67
+ }));
68
+ }
23
69
  async function main() {
24
70
  const args = process.argv.slice(2);
71
+ if (args[0] === "generate") {
72
+ const model = args[1];
73
+ const prompt = readOption(args, "--prompt");
74
+ const apiKey = process.env.NETA_ROUTER_API_KEY;
75
+ if (!model || !prompt || !apiKey) usage();
76
+ const outputDirectory = readOption(args, "--out");
77
+ const content = [{
78
+ type: "text",
79
+ text: prompt
80
+ }, ...readOptions(args, "--image-url").map((url) => ({
81
+ type: "image",
82
+ source: {
83
+ type: "url",
84
+ url
85
+ }
86
+ }))];
87
+ const parameters = Object.fromEntries(readOptions(args, "--param").map(parseParameter));
88
+ const baseUrl = readOption(args, "--base-url");
89
+ const debug = hasFlag(args, "--debug") ? {
90
+ enabled: true,
91
+ includeSensitive: hasFlag(args, "--debug-sensitive"),
92
+ includeResponseBody: !hasFlag(args, "--no-debug-response-body")
93
+ } : void 0;
94
+ const output = await createGenerationClient({
95
+ apiKey,
96
+ ...baseUrl ? { baseUrl } : {},
97
+ ...debug ? { debug } : {}
98
+ }).generate({
99
+ model,
100
+ content,
101
+ parameters
102
+ });
103
+ if (outputDirectory) await writeOutputFiles(outputDirectory, output);
104
+ console.log(JSON.stringify(output.map(outputSummary), null, 2));
105
+ return;
106
+ }
25
107
  if (args[0] !== "models") usage();
26
108
  switch (args[1]) {
27
109
  case "list":
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { listBuiltinGenerationModels } from \"../builtins.js\";\nimport { stringifyGenerationModelDeclaration } from \"../config.js\";\nimport { exportBuiltinModelConfig, exportBuiltinModelConfigs } from \"../export-config.js\";\n\nfunction usage(): never {\n console.log(`neta-generation\n\nUsage:\n neta-generation models list\n neta-generation models export <model> --out <file>\n neta-generation models export-all --out <directory>\n`);\n process.exit(1);\n}\n\nfunction readOption(args: string[], name: string): string | null {\n const index = args.indexOf(name);\n if (index < 0) return null;\n return args[index + 1] ?? null;\n}\n\nasync function main() {\n const args = process.argv.slice(2);\n if (args[0] !== \"models\") usage();\n\n switch (args[1]) {\n case \"list\": {\n for (const model of listBuiltinGenerationModels()) console.log(model.model);\n return;\n }\n case \"export\": {\n const model = args[2];\n const out = readOption(args, \"--out\");\n if (!model || !out) usage();\n await mkdir(dirname(out), { recursive: true });\n await exportBuiltinModelConfig(model, out);\n console.log(out);\n return;\n }\n case \"export-all\": {\n const out = readOption(args, \"--out\");\n if (!out) usage();\n await exportBuiltinModelConfigs(out);\n console.log(out);\n return;\n }\n case \"dump\": {\n const out = readOption(args, \"--out\");\n const models = listBuiltinGenerationModels();\n if (out) {\n await mkdir(out, { recursive: true });\n await Promise.all(\n models.map((model) =>\n writeFile(join(out, `${model.model}.yaml`), stringifyGenerationModelDeclaration(model)),\n ),\n );\n }\n return;\n }\n default:\n usage();\n }\n}\n\nmain().catch((error) => {\n console.error(error instanceof Error ? error.message : String(error));\n process.exit(1);\n});\n"],"mappings":";;;;;;;AAOA,SAAS,QAAe;AACtB,SAAQ,IAAI;;;;;;EAMZ;AACA,SAAQ,KAAK,EAAE;;AAGjB,SAAS,WAAW,MAAgB,MAA6B;CAC/D,MAAM,QAAQ,KAAK,QAAQ,KAAK;AAChC,KAAI,QAAQ,EAAG,QAAO;AACtB,QAAO,KAAK,QAAQ,MAAM;;AAG5B,eAAe,OAAO;CACpB,MAAM,OAAO,QAAQ,KAAK,MAAM,EAAE;AAClC,KAAI,KAAK,OAAO,SAAU,QAAO;AAEjC,SAAQ,KAAK,IAAb;EACE,KAAK;AACH,QAAK,MAAM,SAAS,6BAA6B,CAAE,SAAQ,IAAI,MAAM,MAAM;AAC3E;EAEF,KAAK,UAAU;GACb,MAAM,QAAQ,KAAK;GACnB,MAAM,MAAM,WAAW,MAAM,QAAQ;AACrC,OAAI,CAAC,SAAS,CAAC,IAAK,QAAO;AAC3B,SAAM,MAAM,QAAQ,IAAI,EAAE,EAAE,WAAW,MAAM,CAAC;AAC9C,SAAM,yBAAyB,OAAO,IAAI;AAC1C,WAAQ,IAAI,IAAI;AAChB;;EAEF,KAAK,cAAc;GACjB,MAAM,MAAM,WAAW,MAAM,QAAQ;AACrC,OAAI,CAAC,IAAK,QAAO;AACjB,SAAM,0BAA0B,IAAI;AACpC,WAAQ,IAAI,IAAI;AAChB;;EAEF,KAAK,QAAQ;GACX,MAAM,MAAM,WAAW,MAAM,QAAQ;GACrC,MAAM,SAAS,6BAA6B;AAC5C,OAAI,KAAK;AACP,UAAM,MAAM,KAAK,EAAE,WAAW,MAAM,CAAC;AACrC,UAAM,QAAQ,IACZ,OAAO,KAAK,UACV,UAAU,KAAK,KAAK,GAAG,MAAM,MAAM,OAAO,EAAE,oCAAoC,MAAM,CAAC,CACxF,CACF;;AAEH;;EAEF,QACE,QAAO;;;AAIb,MAAM,CAAC,OAAO,UAAU;AACtB,SAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,CAAC;AACrE,SAAQ,KAAK,EAAE;EACf"}
1
+ {"version":3,"file":"index.js","names":["values: string[]","content: GenerationContentBlock[]"],"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { listBuiltinGenerationModels } from \"../builtins.js\";\nimport { createGenerationClient } from \"../client.js\";\nimport { stringifyGenerationModelDeclaration } from \"../config.js\";\nimport { exportBuiltinModelConfig, exportBuiltinModelConfigs } from \"../export-config.js\";\nimport type { GenerationContentBlock } from \"../types.js\";\n\nfunction usage(): never {\n console.log(`neta-generation\n\nUsage:\n neta-generation generate <model> --prompt <text> [--param key=value] [--image-url <url>] [--out <directory>] [--debug] [--debug-sensitive] [--no-debug-response-body]\n neta-generation models list\n neta-generation models export <model> --out <file>\n neta-generation models export-all --out <directory>\n\nEnvironment:\n NETA_ROUTER_API_KEY API key used by generate\n`);\n process.exit(1);\n}\n\nfunction readOption(args: string[], name: string): string | null {\n const index = args.indexOf(name);\n if (index < 0) return null;\n return args[index + 1] ?? null;\n}\n\nfunction readOptions(args: string[], name: string): string[] {\n const values: string[] = [];\n for (let index = 0; index < args.length; index += 1) {\n const value = args[index + 1];\n if (args[index] === name && value) values.push(value);\n }\n return values;\n}\n\nfunction hasFlag(args: string[], name: string): boolean {\n return args.includes(name);\n}\n\nfunction parseParameterValue(raw: string): unknown {\n if (raw.startsWith(\"json:\")) return JSON.parse(raw.slice(5));\n if (raw === \"true\") return true;\n if (raw === \"false\") return false;\n return raw;\n}\n\nfunction parseParameter(value: string): [string, unknown] {\n const separator = value.indexOf(\"=\");\n if (separator <= 0) throw new Error(`Invalid --param value: ${value}`);\n return [value.slice(0, separator), parseParameterValue(value.slice(separator + 1))];\n}\n\nfunction outputSummary(block: GenerationContentBlock): unknown {\n if (block.type === \"text\") return block;\n return {\n type: block.type,\n source:\n block.source.type === \"url\"\n ? block.source\n : {\n type: \"base64\",\n mediaType: block.source.mediaType,\n bytes: Buffer.byteLength(block.source.data, \"base64\"),\n },\n meta: block.meta,\n };\n}\n\nasync function writeOutputFiles(directory: string, output: GenerationContentBlock[]): Promise<void> {\n await mkdir(directory, { recursive: true });\n await Promise.all(\n output.map(async (block, index) => {\n if (\n (block.type !== \"image\" && block.type !== \"video\" && block.type !== \"audio\") ||\n block.source.type !== \"base64\"\n ) {\n return;\n }\n const extension = block.source.mediaType.split(\"/\")[1]?.split(\"+\")[0] || block.type;\n await writeFile(\n join(directory, `${String(index + 1).padStart(2, \"0\")}.${extension}`),\n Buffer.from(block.source.data, \"base64\"),\n );\n }),\n );\n}\n\nasync function main() {\n const args = process.argv.slice(2);\n if (args[0] === \"generate\") {\n const model = args[1];\n const prompt = readOption(args, \"--prompt\");\n const apiKey = process.env.NETA_ROUTER_API_KEY;\n if (!model || !prompt || !apiKey) usage();\n\n const outputDirectory = readOption(args, \"--out\");\n const content: GenerationContentBlock[] = [\n { type: \"text\", text: prompt },\n ...readOptions(args, \"--image-url\").map((url) => ({\n type: \"image\" as const,\n source: { type: \"url\" as const, url },\n })),\n ];\n const parameters = Object.fromEntries(readOptions(args, \"--param\").map(parseParameter));\n const baseUrl = readOption(args, \"--base-url\");\n const debug = hasFlag(args, \"--debug\")\n ? {\n enabled: true,\n includeSensitive: hasFlag(args, \"--debug-sensitive\"),\n includeResponseBody: !hasFlag(args, \"--no-debug-response-body\"),\n }\n : undefined;\n const client = createGenerationClient({\n apiKey,\n ...(baseUrl ? { baseUrl } : {}),\n ...(debug ? { debug } : {}),\n });\n const output = await client.generate({ model, content, parameters });\n if (outputDirectory) await writeOutputFiles(outputDirectory, output);\n console.log(JSON.stringify(output.map(outputSummary), null, 2));\n return;\n }\n\n if (args[0] !== \"models\") usage();\n\n switch (args[1]) {\n case \"list\": {\n for (const model of listBuiltinGenerationModels()) console.log(model.model);\n return;\n }\n case \"export\": {\n const model = args[2];\n const out = readOption(args, \"--out\");\n if (!model || !out) usage();\n await mkdir(dirname(out), { recursive: true });\n await exportBuiltinModelConfig(model, out);\n console.log(out);\n return;\n }\n case \"export-all\": {\n const out = readOption(args, \"--out\");\n if (!out) usage();\n await exportBuiltinModelConfigs(out);\n console.log(out);\n return;\n }\n case \"dump\": {\n const out = readOption(args, \"--out\");\n const models = listBuiltinGenerationModels();\n if (out) {\n await mkdir(out, { recursive: true });\n await Promise.all(\n models.map((model) =>\n writeFile(join(out, `${model.model}.yaml`), stringifyGenerationModelDeclaration(model)),\n ),\n );\n }\n return;\n }\n default:\n usage();\n }\n}\n\nmain().catch((error) => {\n console.error(error instanceof Error ? error.message : String(error));\n process.exit(1);\n});\n"],"mappings":"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"}