@diologue/local-agent 0.1.0 → 0.1.1

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/dist/cli.mjs CHANGED
@@ -1351,6 +1351,366 @@ var translateResponse = (response, id, modelEcho) => {
1351
1351
  };
1352
1352
  };
1353
1353
 
1354
+ // src/shim/responses-translator.ts
1355
+ var flattenContent = (content) => {
1356
+ if (typeof content === "string") return content;
1357
+ return content.map((part) => {
1358
+ if (part.type === "input_text" || part.type === "output_text") {
1359
+ return part.text ?? "";
1360
+ }
1361
+ return "";
1362
+ }).join("");
1363
+ };
1364
+ var responsesToChatCompletions = (req) => {
1365
+ const messages = [];
1366
+ for (const item of req.input) {
1367
+ if ("type" in item && item.type === "function_call") {
1368
+ messages.push({
1369
+ role: "assistant",
1370
+ content: null,
1371
+ tool_calls: [
1372
+ {
1373
+ id: item.call_id,
1374
+ type: "function",
1375
+ function: { name: item.name, arguments: item.arguments }
1376
+ }
1377
+ ]
1378
+ });
1379
+ continue;
1380
+ }
1381
+ if ("type" in item && item.type === "function_call_output") {
1382
+ messages.push({
1383
+ role: "tool",
1384
+ content: item.output,
1385
+ tool_call_id: item.call_id
1386
+ });
1387
+ continue;
1388
+ }
1389
+ const role = item.role;
1390
+ const content = flattenContent(item.content);
1391
+ messages.push({ role, content });
1392
+ }
1393
+ const tools = req.tools?.map((t) => ({
1394
+ type: "function",
1395
+ function: {
1396
+ name: t.name,
1397
+ description: t.description,
1398
+ parameters: t.parameters
1399
+ }
1400
+ }));
1401
+ let toolChoice;
1402
+ if (typeof req.tool_choice === "string") {
1403
+ toolChoice = req.tool_choice;
1404
+ } else if (req.tool_choice && typeof req.tool_choice === "object") {
1405
+ toolChoice = {
1406
+ type: "function",
1407
+ function: { name: req.tool_choice.name }
1408
+ };
1409
+ }
1410
+ return {
1411
+ model: req.model,
1412
+ messages,
1413
+ tools,
1414
+ tool_choice: toolChoice,
1415
+ temperature: req.temperature,
1416
+ stream: req.stream,
1417
+ // Pass max_output_tokens through as max_tokens for downstream
1418
+ // providers that honour it. Chat Completions naming.
1419
+ max_tokens: req.max_output_tokens
1420
+ };
1421
+ };
1422
+ var randomId = (prefix) => `${prefix}_${Math.random().toString(36).slice(2, 12)}`;
1423
+ var ResponsesStreamState = class {
1424
+ responseId;
1425
+ model;
1426
+ createdAt;
1427
+ outputIndex = 0;
1428
+ /** Open message item carrying assistant text, if any. */
1429
+ text = null;
1430
+ /** Open function_call items, keyed by the broker's tool_call index
1431
+ * (the model's call position, not our output_index). */
1432
+ tools = /* @__PURE__ */ new Map();
1433
+ finished = false;
1434
+ /** Set when broker emits `complete`. We carry usage + finish_reason
1435
+ * into the final response.completed payload. */
1436
+ finalUsage;
1437
+ finalStatus = "completed";
1438
+ constructor(model, id) {
1439
+ this.responseId = id ?? randomId("resp");
1440
+ this.model = model;
1441
+ this.createdAt = Math.floor(Date.now() / 1e3);
1442
+ }
1443
+ /** Emit the response.created + response.in_progress pair. Call once
1444
+ * before any chunks. */
1445
+ start() {
1446
+ const responseObj = {
1447
+ id: this.responseId,
1448
+ object: "response",
1449
+ created_at: this.createdAt,
1450
+ status: "in_progress",
1451
+ model: this.model,
1452
+ output: [],
1453
+ usage: null
1454
+ };
1455
+ return [
1456
+ { event: "response.created", data: { type: "response.created", response: responseObj } },
1457
+ { event: "response.in_progress", data: { type: "response.in_progress", response: responseObj } }
1458
+ ];
1459
+ }
1460
+ /** Translate a single broker chunk. Some chunks emit multiple events
1461
+ * (opening a new item requires output_item.added + content_part.added
1462
+ * before the delta itself). */
1463
+ chunk(c) {
1464
+ if (this.finished) return [];
1465
+ switch (c.type) {
1466
+ case "text_delta":
1467
+ return this.handleTextDelta(c.text);
1468
+ case "tool_call_delta":
1469
+ return this.handleToolCallDelta(c);
1470
+ case "complete":
1471
+ {
1472
+ const tu = c.payload?.tokenUsage;
1473
+ if (tu) {
1474
+ const input = tu.input ?? 0;
1475
+ const output = tu.output ?? 0;
1476
+ this.finalUsage = {
1477
+ input_tokens: input,
1478
+ output_tokens: output,
1479
+ total_tokens: input + output
1480
+ };
1481
+ }
1482
+ }
1483
+ return [];
1484
+ case "error":
1485
+ this.finalStatus = "failed";
1486
+ return [];
1487
+ }
1488
+ }
1489
+ /** Close any open items + emit the terminal response.completed event. */
1490
+ finish() {
1491
+ if (this.finished) return [];
1492
+ this.finished = true;
1493
+ const out = [];
1494
+ out.push(...this.closeTextItem());
1495
+ out.push(...this.closeAllToolItems());
1496
+ const responseObj = {
1497
+ id: this.responseId,
1498
+ object: "response",
1499
+ created_at: this.createdAt,
1500
+ status: this.finalStatus,
1501
+ model: this.model,
1502
+ output: [],
1503
+ // For brevity — the AI SDK only needs status + usage at completion.
1504
+ usage: this.finalUsage ?? null
1505
+ };
1506
+ out.push({
1507
+ event: "response.completed",
1508
+ data: { type: "response.completed", response: responseObj }
1509
+ });
1510
+ return out;
1511
+ }
1512
+ // ---- internals ----
1513
+ handleTextDelta(text) {
1514
+ if (!this.text) {
1515
+ const itemId = randomId("msg");
1516
+ const outputIndex = this.outputIndex++;
1517
+ const contentIndex = 0;
1518
+ this.text = { itemId, outputIndex, contentIndex, buffer: text };
1519
+ return [
1520
+ {
1521
+ event: "response.output_item.added",
1522
+ data: {
1523
+ type: "response.output_item.added",
1524
+ output_index: outputIndex,
1525
+ item: {
1526
+ id: itemId,
1527
+ type: "message",
1528
+ status: "in_progress",
1529
+ role: "assistant",
1530
+ content: []
1531
+ }
1532
+ }
1533
+ },
1534
+ {
1535
+ event: "response.content_part.added",
1536
+ data: {
1537
+ type: "response.content_part.added",
1538
+ item_id: itemId,
1539
+ output_index: outputIndex,
1540
+ content_index: contentIndex,
1541
+ part: { type: "output_text", text: "" }
1542
+ }
1543
+ },
1544
+ {
1545
+ event: "response.output_text.delta",
1546
+ data: {
1547
+ type: "response.output_text.delta",
1548
+ item_id: itemId,
1549
+ output_index: outputIndex,
1550
+ content_index: contentIndex,
1551
+ delta: text
1552
+ }
1553
+ }
1554
+ ];
1555
+ }
1556
+ this.text.buffer += text;
1557
+ return [
1558
+ {
1559
+ event: "response.output_text.delta",
1560
+ data: {
1561
+ type: "response.output_text.delta",
1562
+ item_id: this.text.itemId,
1563
+ output_index: this.text.outputIndex,
1564
+ content_index: this.text.contentIndex,
1565
+ delta: text
1566
+ }
1567
+ }
1568
+ ];
1569
+ }
1570
+ handleToolCallDelta(c) {
1571
+ const events = [];
1572
+ events.push(...this.closeTextItem());
1573
+ let entry = this.tools.get(c.index);
1574
+ if (!entry) {
1575
+ entry = {
1576
+ itemId: randomId("fc"),
1577
+ outputIndex: this.outputIndex++,
1578
+ callId: c.id ?? randomId("call"),
1579
+ name: c.name ?? "",
1580
+ arguments: "",
1581
+ emittedAdded: false
1582
+ };
1583
+ this.tools.set(c.index, entry);
1584
+ } else {
1585
+ if (c.id) entry.callId = c.id;
1586
+ if (c.name) entry.name = c.name;
1587
+ }
1588
+ if (!entry.emittedAdded && entry.name) {
1589
+ entry.emittedAdded = true;
1590
+ events.push({
1591
+ event: "response.output_item.added",
1592
+ data: {
1593
+ type: "response.output_item.added",
1594
+ output_index: entry.outputIndex,
1595
+ item: {
1596
+ id: entry.itemId,
1597
+ type: "function_call",
1598
+ status: "in_progress",
1599
+ name: entry.name,
1600
+ call_id: entry.callId,
1601
+ arguments: ""
1602
+ }
1603
+ }
1604
+ });
1605
+ }
1606
+ if (c.argumentsDelta !== void 0 && c.argumentsDelta.length > 0) {
1607
+ entry.arguments += c.argumentsDelta;
1608
+ if (entry.emittedAdded) {
1609
+ events.push({
1610
+ event: "response.function_call_arguments.delta",
1611
+ data: {
1612
+ type: "response.function_call_arguments.delta",
1613
+ item_id: entry.itemId,
1614
+ output_index: entry.outputIndex,
1615
+ delta: c.argumentsDelta
1616
+ }
1617
+ });
1618
+ }
1619
+ }
1620
+ return events;
1621
+ }
1622
+ closeTextItem() {
1623
+ if (!this.text) return [];
1624
+ const { itemId, outputIndex, contentIndex, buffer } = this.text;
1625
+ this.text = null;
1626
+ return [
1627
+ {
1628
+ event: "response.output_text.done",
1629
+ data: {
1630
+ type: "response.output_text.done",
1631
+ item_id: itemId,
1632
+ output_index: outputIndex,
1633
+ content_index: contentIndex,
1634
+ text: buffer
1635
+ }
1636
+ },
1637
+ {
1638
+ event: "response.content_part.done",
1639
+ data: {
1640
+ type: "response.content_part.done",
1641
+ item_id: itemId,
1642
+ output_index: outputIndex,
1643
+ content_index: contentIndex,
1644
+ part: { type: "output_text", text: buffer }
1645
+ }
1646
+ },
1647
+ {
1648
+ event: "response.output_item.done",
1649
+ data: {
1650
+ type: "response.output_item.done",
1651
+ output_index: outputIndex,
1652
+ item: {
1653
+ id: itemId,
1654
+ type: "message",
1655
+ status: "completed",
1656
+ role: "assistant",
1657
+ content: [{ type: "output_text", text: buffer }]
1658
+ }
1659
+ }
1660
+ }
1661
+ ];
1662
+ }
1663
+ closeAllToolItems() {
1664
+ const out = [];
1665
+ for (const entry of this.tools.values()) {
1666
+ if (!entry.emittedAdded) {
1667
+ entry.emittedAdded = true;
1668
+ out.push({
1669
+ event: "response.output_item.added",
1670
+ data: {
1671
+ type: "response.output_item.added",
1672
+ output_index: entry.outputIndex,
1673
+ item: {
1674
+ id: entry.itemId,
1675
+ type: "function_call",
1676
+ status: "in_progress",
1677
+ name: entry.name || "unknown",
1678
+ call_id: entry.callId,
1679
+ arguments: ""
1680
+ }
1681
+ }
1682
+ });
1683
+ }
1684
+ out.push({
1685
+ event: "response.function_call_arguments.done",
1686
+ data: {
1687
+ type: "response.function_call_arguments.done",
1688
+ item_id: entry.itemId,
1689
+ output_index: entry.outputIndex,
1690
+ arguments: entry.arguments
1691
+ }
1692
+ });
1693
+ out.push({
1694
+ event: "response.output_item.done",
1695
+ data: {
1696
+ type: "response.output_item.done",
1697
+ output_index: entry.outputIndex,
1698
+ item: {
1699
+ id: entry.itemId,
1700
+ type: "function_call",
1701
+ status: "completed",
1702
+ name: entry.name || "unknown",
1703
+ call_id: entry.callId,
1704
+ arguments: entry.arguments
1705
+ }
1706
+ }
1707
+ });
1708
+ }
1709
+ this.tools.clear();
1710
+ return out;
1711
+ }
1712
+ };
1713
+
1354
1714
  // src/shim/active-broker.ts
1355
1715
  import { randomUUID as randomUUID2 } from "node:crypto";
1356
1716
  var byToken = /* @__PURE__ */ new Map();
@@ -1545,6 +1905,129 @@ var createLlmShimRouter = () => {
1545
1905
  res.json(out);
1546
1906
  }
1547
1907
  );
1908
+ router.post("/v1/responses", async (req, res) => {
1909
+ const token = extractBearer(req);
1910
+ if (!token) {
1911
+ sendOpenAIError(res, 401, "Missing Authorization bearer", "auth_error");
1912
+ return;
1913
+ }
1914
+ const broker = getActiveBrokerByToken(token);
1915
+ if (!broker) {
1916
+ sendOpenAIError(
1917
+ res,
1918
+ 401,
1919
+ "No active coding-agent stream is currently authorised on this helper.",
1920
+ "auth_error"
1921
+ );
1922
+ return;
1923
+ }
1924
+ const body = req.body;
1925
+ if (!body || typeof body !== "object" || !Array.isArray(body.input) || body.input.length === 0) {
1926
+ sendOpenAIError(res, 400, "input[] is required and must be non-empty");
1927
+ return;
1928
+ }
1929
+ const chatRequest = responsesToChatCompletions(body);
1930
+ const brokerRequest = translateRequest(chatRequest);
1931
+ const stream = body.stream === true;
1932
+ if (stream) {
1933
+ res.setHeader("Content-Type", "text/event-stream");
1934
+ res.setHeader("Cache-Control", "no-cache, no-transform");
1935
+ res.setHeader("Connection", "keep-alive");
1936
+ res.flushHeaders?.();
1937
+ const state = new ResponsesStreamState(body.model);
1938
+ const writeEvent2 = (ev) => {
1939
+ res.write(`event: ${ev.event}
1940
+ `);
1941
+ res.write(`data: ${JSON.stringify(ev.data)}
1942
+
1943
+ `);
1944
+ };
1945
+ for (const ev of state.start()) writeEvent2(ev);
1946
+ const observer = (chunk) => {
1947
+ for (const ev of state.chunk(chunk)) writeEvent2(ev);
1948
+ };
1949
+ try {
1950
+ await broker.request(brokerRequest, observer);
1951
+ } catch (err) {
1952
+ writeEvent2({
1953
+ event: "response.failed",
1954
+ data: {
1955
+ type: "response.failed",
1956
+ response: {
1957
+ id: `resp_${Math.random().toString(36).slice(2, 12)}`,
1958
+ object: "response",
1959
+ status: "failed",
1960
+ model: body.model,
1961
+ error: {
1962
+ code: "broker_error",
1963
+ message: err instanceof Error ? err.message : "Broker call failed"
1964
+ }
1965
+ }
1966
+ }
1967
+ });
1968
+ res.write("data: [DONE]\n\n");
1969
+ res.end();
1970
+ return;
1971
+ }
1972
+ for (const ev of state.finish()) writeEvent2(ev);
1973
+ res.write("data: [DONE]\n\n");
1974
+ res.end();
1975
+ return;
1976
+ }
1977
+ let brokerResponse;
1978
+ try {
1979
+ brokerResponse = await broker.request(brokerRequest);
1980
+ } catch (err) {
1981
+ sendOpenAIError(
1982
+ res,
1983
+ 502,
1984
+ err instanceof Error ? err.message : "Broker call failed",
1985
+ "api_error"
1986
+ );
1987
+ return;
1988
+ }
1989
+ if (brokerResponse.error) {
1990
+ sendOpenAIError(res, 502, brokerResponse.error, "api_error");
1991
+ return;
1992
+ }
1993
+ const completionId = `resp_${Math.random().toString(36).slice(2, 12)}`;
1994
+ const chatResponse = translateResponse(
1995
+ brokerResponse,
1996
+ completionId,
1997
+ body.model
1998
+ );
1999
+ const text = chatResponse.choices[0]?.message?.content ?? "";
2000
+ const toolCalls = chatResponse.choices[0]?.message?.tool_calls ?? [];
2001
+ const output = [];
2002
+ if (text) {
2003
+ output.push({
2004
+ id: `msg_${Math.random().toString(36).slice(2, 12)}`,
2005
+ type: "message",
2006
+ status: "completed",
2007
+ role: "assistant",
2008
+ content: [{ type: "output_text", text }]
2009
+ });
2010
+ }
2011
+ for (const tc of toolCalls) {
2012
+ output.push({
2013
+ id: `fc_${Math.random().toString(36).slice(2, 12)}`,
2014
+ type: "function_call",
2015
+ status: "completed",
2016
+ name: tc.function.name,
2017
+ call_id: tc.id,
2018
+ arguments: tc.function.arguments
2019
+ });
2020
+ }
2021
+ res.json({
2022
+ id: completionId,
2023
+ object: "response",
2024
+ created_at: Math.floor(Date.now() / 1e3),
2025
+ status: "completed",
2026
+ model: body.model,
2027
+ output,
2028
+ usage: chatResponse.usage
2029
+ });
2030
+ });
1548
2031
  return router;
1549
2032
  };
1550
2033