@providerprotocol/ai 0.0.18 → 0.0.19

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.
@@ -1,3 +1,6 @@
1
+ import {
2
+ Image
3
+ } from "../chunk-WAKD3OO5.js";
1
4
  import {
2
5
  AssistantMessage,
3
6
  isAssistantMessage,
@@ -1381,6 +1384,303 @@ function createEmbeddingHandler() {
1381
1384
  };
1382
1385
  }
1383
1386
 
1387
+ // src/providers/openai/image.ts
1388
+ var OPENAI_IMAGES_API_URL = "https://api.openai.com/v1/images/generations";
1389
+ var OPENAI_IMAGES_EDIT_URL = "https://api.openai.com/v1/images/edits";
1390
+ function getCapabilities(modelId) {
1391
+ const isGptImage = modelId.startsWith("gpt-image");
1392
+ const isDalle2 = modelId === "dall-e-2";
1393
+ return {
1394
+ generate: true,
1395
+ streaming: isGptImage,
1396
+ edit: true,
1397
+ maxImages: isDalle2 ? 10 : isGptImage ? 10 : 1
1398
+ };
1399
+ }
1400
+ function createImageHandler() {
1401
+ let providerRef = null;
1402
+ return {
1403
+ _setProvider(provider) {
1404
+ providerRef = provider;
1405
+ },
1406
+ bind(modelId) {
1407
+ if (!providerRef) {
1408
+ throw new UPPError(
1409
+ "Provider reference not set. Handler must be used with createProvider().",
1410
+ "INVALID_REQUEST",
1411
+ "openai",
1412
+ "image"
1413
+ );
1414
+ }
1415
+ const capabilities = getCapabilities(modelId);
1416
+ const model = {
1417
+ modelId,
1418
+ capabilities,
1419
+ get provider() {
1420
+ return providerRef;
1421
+ },
1422
+ async generate(request) {
1423
+ return executeGenerate(modelId, request);
1424
+ },
1425
+ async edit(request) {
1426
+ return executeEdit(modelId, request);
1427
+ }
1428
+ };
1429
+ if (capabilities.streaming) {
1430
+ model.stream = function(request) {
1431
+ return executeStream(modelId, request);
1432
+ };
1433
+ }
1434
+ return model;
1435
+ }
1436
+ };
1437
+ }
1438
+ async function executeGenerate(modelId, request) {
1439
+ const apiKey = await resolveApiKey(
1440
+ request.config,
1441
+ "OPENAI_API_KEY",
1442
+ "openai",
1443
+ "image"
1444
+ );
1445
+ const baseUrl = request.config.baseUrl ? `${request.config.baseUrl.replace(/\/$/, "")}/v1/images/generations` : OPENAI_IMAGES_API_URL;
1446
+ const body = {
1447
+ model: modelId,
1448
+ prompt: request.prompt
1449
+ };
1450
+ if (request.params) {
1451
+ const { n, size, quality, style, background, output_format, output_compression, response_format, moderation, user } = request.params;
1452
+ if (n !== void 0) body.n = n;
1453
+ if (size !== void 0) body.size = size;
1454
+ if (quality !== void 0) body.quality = quality;
1455
+ if (style !== void 0) body.style = style;
1456
+ if (background !== void 0) body.background = background;
1457
+ if (output_format !== void 0) body.output_format = output_format;
1458
+ if (output_compression !== void 0) body.output_compression = output_compression;
1459
+ if (response_format !== void 0) body.response_format = response_format;
1460
+ if (moderation !== void 0) body.moderation = moderation;
1461
+ if (user !== void 0) body.user = user;
1462
+ }
1463
+ const headers = {
1464
+ "Content-Type": "application/json",
1465
+ Authorization: `Bearer ${apiKey}`
1466
+ };
1467
+ if (request.config.headers) {
1468
+ for (const [key, value] of Object.entries(request.config.headers)) {
1469
+ if (value !== void 0) {
1470
+ headers[key] = value;
1471
+ }
1472
+ }
1473
+ }
1474
+ const response = await doFetch(baseUrl, {
1475
+ method: "POST",
1476
+ headers,
1477
+ body: JSON.stringify(body),
1478
+ signal: request.signal
1479
+ }, request.config, "openai", "image");
1480
+ const data = await response.json();
1481
+ return transformResponse3(data);
1482
+ }
1483
+ async function executeEdit(modelId, request) {
1484
+ const apiKey = await resolveApiKey(
1485
+ request.config,
1486
+ "OPENAI_API_KEY",
1487
+ "openai",
1488
+ "image"
1489
+ );
1490
+ const baseUrl = request.config.baseUrl ? `${request.config.baseUrl.replace(/\/$/, "")}/v1/images/edits` : OPENAI_IMAGES_EDIT_URL;
1491
+ const formData = new FormData();
1492
+ formData.append("model", modelId);
1493
+ formData.append("prompt", request.prompt);
1494
+ const imageBytes = request.image.toBytes();
1495
+ const imageBlob = new Blob([imageBytes], { type: request.image.mimeType });
1496
+ formData.append("image", imageBlob, "image.png");
1497
+ if (request.mask) {
1498
+ const maskBytes = request.mask.toBytes();
1499
+ const maskBlob = new Blob([maskBytes], { type: request.mask.mimeType });
1500
+ formData.append("mask", maskBlob, "mask.png");
1501
+ }
1502
+ if (request.params) {
1503
+ const { n, size, response_format, user } = request.params;
1504
+ if (n !== void 0) formData.append("n", String(n));
1505
+ if (size !== void 0) formData.append("size", size);
1506
+ if (response_format !== void 0) formData.append("response_format", response_format);
1507
+ if (user !== void 0) formData.append("user", user);
1508
+ }
1509
+ const headers = {
1510
+ Authorization: `Bearer ${apiKey}`
1511
+ };
1512
+ if (request.config.headers) {
1513
+ for (const [key, value] of Object.entries(request.config.headers)) {
1514
+ if (value !== void 0) {
1515
+ headers[key] = value;
1516
+ }
1517
+ }
1518
+ }
1519
+ const response = await doFetch(baseUrl, {
1520
+ method: "POST",
1521
+ headers,
1522
+ body: formData,
1523
+ signal: request.signal
1524
+ }, request.config, "openai", "image");
1525
+ const data = await response.json();
1526
+ return transformResponse3(data);
1527
+ }
1528
+ function executeStream(modelId, request) {
1529
+ const abortController = new AbortController();
1530
+ let resolveResponse;
1531
+ let rejectResponse;
1532
+ const responsePromise = new Promise((resolve, reject) => {
1533
+ resolveResponse = resolve;
1534
+ rejectResponse = reject;
1535
+ });
1536
+ async function* generateStream() {
1537
+ try {
1538
+ const apiKey = await resolveApiKey(
1539
+ request.config,
1540
+ "OPENAI_API_KEY",
1541
+ "openai",
1542
+ "image"
1543
+ );
1544
+ const baseUrl = request.config.baseUrl ? `${request.config.baseUrl.replace(/\/$/, "")}/v1/images/generations` : OPENAI_IMAGES_API_URL;
1545
+ const body = {
1546
+ model: modelId,
1547
+ prompt: request.prompt,
1548
+ stream: true
1549
+ };
1550
+ if (request.params) {
1551
+ const { n, size, quality, background, output_format, partial_images, moderation, user } = request.params;
1552
+ if (n !== void 0) body.n = n;
1553
+ if (size !== void 0) body.size = size;
1554
+ if (quality !== void 0) body.quality = quality;
1555
+ if (background !== void 0) body.background = background;
1556
+ if (output_format !== void 0) body.output_format = output_format;
1557
+ if (partial_images !== void 0) body.partial_images = partial_images;
1558
+ if (moderation !== void 0) body.moderation = moderation;
1559
+ if (user !== void 0) body.user = user;
1560
+ }
1561
+ const headers = {
1562
+ "Content-Type": "application/json",
1563
+ Authorization: `Bearer ${apiKey}`,
1564
+ Accept: "text/event-stream"
1565
+ };
1566
+ if (request.config.headers) {
1567
+ for (const [key, value] of Object.entries(request.config.headers)) {
1568
+ if (value !== void 0) {
1569
+ headers[key] = value;
1570
+ }
1571
+ }
1572
+ }
1573
+ const mergedSignal = request.signal ? AbortSignal.any([abortController.signal, request.signal]) : abortController.signal;
1574
+ const response = await doFetch(baseUrl, {
1575
+ method: "POST",
1576
+ headers,
1577
+ body: JSON.stringify(body),
1578
+ signal: mergedSignal
1579
+ }, request.config, "openai", "image");
1580
+ const reader = response.body?.getReader();
1581
+ if (!reader) {
1582
+ throw new UPPError(
1583
+ "No response body for streaming",
1584
+ "PROVIDER_ERROR",
1585
+ "openai",
1586
+ "image"
1587
+ );
1588
+ }
1589
+ const decoder = new TextDecoder();
1590
+ let buffer = "";
1591
+ const generatedImages = [];
1592
+ let responseMetadata = {};
1593
+ while (true) {
1594
+ const { done, value } = await reader.read();
1595
+ if (done) break;
1596
+ buffer += decoder.decode(value, { stream: true });
1597
+ const lines = buffer.split("\n");
1598
+ buffer = lines.pop() ?? "";
1599
+ for (const line of lines) {
1600
+ if (line.startsWith("data: ")) {
1601
+ const data = line.slice(6);
1602
+ if (data === "[DONE]") {
1603
+ continue;
1604
+ }
1605
+ try {
1606
+ const chunk = JSON.parse(data);
1607
+ if (chunk.type === "image_generation.partial_image" && chunk.data?.b64_json) {
1608
+ const previewImage = Image.fromBase64(chunk.data.b64_json, "image/png");
1609
+ yield {
1610
+ type: "preview",
1611
+ image: previewImage,
1612
+ index: chunk.index ?? 0
1613
+ };
1614
+ } else if (chunk.type === "image_generation.completed" && chunk.data) {
1615
+ const image = chunk.data.b64_json ? Image.fromBase64(chunk.data.b64_json, "image/png") : Image.fromUrl(chunk.data.url ?? "", "image/png");
1616
+ const genImage = {
1617
+ image,
1618
+ metadata: chunk.data.revised_prompt ? { revised_prompt: chunk.data.revised_prompt } : void 0
1619
+ };
1620
+ generatedImages.push(genImage);
1621
+ yield {
1622
+ type: "complete",
1623
+ image: genImage,
1624
+ index: chunk.index ?? generatedImages.length - 1
1625
+ };
1626
+ } else if (chunk.type === "response.done") {
1627
+ responseMetadata = chunk.data;
1628
+ }
1629
+ } catch {
1630
+ }
1631
+ }
1632
+ }
1633
+ }
1634
+ resolveResponse({
1635
+ images: generatedImages,
1636
+ metadata: responseMetadata,
1637
+ usage: {
1638
+ imagesGenerated: generatedImages.length
1639
+ }
1640
+ });
1641
+ } catch (error) {
1642
+ rejectResponse(error);
1643
+ throw error;
1644
+ }
1645
+ }
1646
+ const generator = generateStream();
1647
+ return {
1648
+ [Symbol.asyncIterator]: () => generator,
1649
+ response: responsePromise
1650
+ };
1651
+ }
1652
+ function transformResponse3(data) {
1653
+ const images = data.data.map((item) => {
1654
+ let image;
1655
+ if (item.b64_json) {
1656
+ image = Image.fromBase64(item.b64_json, "image/png");
1657
+ } else if (item.url) {
1658
+ image = Image.fromUrl(item.url, "image/png");
1659
+ } else {
1660
+ throw new UPPError(
1661
+ "No image data in response",
1662
+ "PROVIDER_ERROR",
1663
+ "openai",
1664
+ "image"
1665
+ );
1666
+ }
1667
+ return {
1668
+ image,
1669
+ metadata: item.revised_prompt ? { revised_prompt: item.revised_prompt } : void 0
1670
+ };
1671
+ });
1672
+ return {
1673
+ images,
1674
+ usage: data.usage ? {
1675
+ imagesGenerated: images.length,
1676
+ inputTokens: data.usage.input_tokens,
1677
+ outputTokens: data.usage.output_tokens
1678
+ } : {
1679
+ imagesGenerated: images.length
1680
+ }
1681
+ };
1682
+ }
1683
+
1384
1684
  // src/providers/openai/types.ts
1385
1685
  function webSearchTool(options) {
1386
1686
  if (options) {
@@ -1454,6 +1754,7 @@ function createOpenAIProvider() {
1454
1754
  const responsesHandler = createResponsesLLMHandler();
1455
1755
  const completionsHandler = createCompletionsLLMHandler();
1456
1756
  const embeddingHandler = createEmbeddingHandler();
1757
+ const imageHandler = createImageHandler();
1457
1758
  const fn = function(modelId, options) {
1458
1759
  const apiMode = options?.api ?? "responses";
1459
1760
  currentApiMode = apiMode;
@@ -1463,7 +1764,8 @@ function createOpenAIProvider() {
1463
1764
  get llm() {
1464
1765
  return currentApiMode === "completions" ? completionsHandler : responsesHandler;
1465
1766
  },
1466
- embedding: embeddingHandler
1767
+ embedding: embeddingHandler,
1768
+ image: imageHandler
1467
1769
  };
1468
1770
  Object.defineProperties(fn, {
1469
1771
  name: {
@@ -1486,6 +1788,7 @@ function createOpenAIProvider() {
1486
1788
  responsesHandler._setProvider?.(provider);
1487
1789
  completionsHandler._setProvider?.(provider);
1488
1790
  embeddingHandler._setProvider?.(provider);
1791
+ imageHandler._setProvider?.(provider);
1489
1792
  return provider;
1490
1793
  }
1491
1794
  var openai = createOpenAIProvider();