@prompty/core 2.0.0-alpha.1 → 2.0.0-alpha.3

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/index.js CHANGED
@@ -4445,6 +4445,114 @@ async function execute(prompt, inputs, options) {
4445
4445
  return result;
4446
4446
  });
4447
4447
  }
4448
+ function isAsyncIterable(value) {
4449
+ return value != null && typeof value === "object" && Symbol.asyncIterator in value;
4450
+ }
4451
+ function isToolCallLike(item) {
4452
+ return typeof item === "object" && item !== null && "id" in item && "name" in item && "arguments" in item;
4453
+ }
4454
+ async function consumeStream(agent, response) {
4455
+ const processed = await process2(agent, response);
4456
+ const toolCalls = [];
4457
+ const textParts = [];
4458
+ if (isAsyncIterable(processed)) {
4459
+ for await (const item of processed) {
4460
+ if (isToolCallLike(item)) {
4461
+ toolCalls.push(item);
4462
+ } else if (typeof item === "string") {
4463
+ textParts.push(item);
4464
+ }
4465
+ }
4466
+ } else if (typeof processed === "string") {
4467
+ textParts.push(processed);
4468
+ }
4469
+ return { toolCalls, content: textParts.join("") };
4470
+ }
4471
+ async function buildToolMessagesFromCalls(toolCalls, textContent, tools, agent, parentEmit) {
4472
+ const provider = resolveProvider(agent);
4473
+ const apiType = agent.model?.apiType || "chat";
4474
+ const messages = [];
4475
+ const toolInputs = [];
4476
+ if (provider === "anthropic") {
4477
+ const rawContent = [];
4478
+ if (textContent) rawContent.push({ type: "text", text: textContent });
4479
+ for (const tc of toolCalls) {
4480
+ rawContent.push({
4481
+ type: "tool_use",
4482
+ id: tc.id,
4483
+ name: tc.name,
4484
+ input: JSON.parse(tc.arguments)
4485
+ });
4486
+ }
4487
+ messages.push(
4488
+ new Message("assistant", textContent ? [text(textContent)] : [], { content: rawContent })
4489
+ );
4490
+ } else if (apiType === "responses") {
4491
+ for (const tc of toolCalls) {
4492
+ messages.push(
4493
+ new Message("assistant", [], {
4494
+ responses_function_call: {
4495
+ type: "function_call",
4496
+ call_id: tc.id,
4497
+ name: tc.name,
4498
+ arguments: tc.arguments
4499
+ }
4500
+ })
4501
+ );
4502
+ }
4503
+ } else {
4504
+ const rawToolCalls = toolCalls.map((tc) => ({
4505
+ id: tc.id,
4506
+ type: "function",
4507
+ function: { name: tc.name, arguments: tc.arguments }
4508
+ }));
4509
+ messages.push(
4510
+ new Message("assistant", textContent ? [text(textContent)] : [], {
4511
+ tool_calls: rawToolCalls
4512
+ })
4513
+ );
4514
+ }
4515
+ const toolResultBlocks = [];
4516
+ for (const tc of toolCalls) {
4517
+ let result;
4518
+ let parsedArgs;
4519
+ try {
4520
+ parsedArgs = JSON.parse(tc.arguments);
4521
+ const toolFn = tools[tc.name];
4522
+ if (!toolFn) {
4523
+ result = `Error: tool "${tc.name}" not found`;
4524
+ } else {
4525
+ const toolResult = await traceSpan(tc.name, async (toolEmit) => {
4526
+ toolEmit("signature", `prompty.tool.${tc.name}`);
4527
+ toolEmit("description", `Execute tool: ${tc.name}`);
4528
+ toolEmit("inputs", { arguments: parsedArgs, id: tc.id });
4529
+ const r = await toolFn(...Array.isArray(parsedArgs) ? parsedArgs : [parsedArgs]);
4530
+ const str = typeof r === "string" ? r : JSON.stringify(r);
4531
+ toolEmit("result", str);
4532
+ return str;
4533
+ });
4534
+ result = toolResult;
4535
+ }
4536
+ } catch (err) {
4537
+ result = `Error: ${err instanceof Error ? err.message : String(err)}`;
4538
+ }
4539
+ toolInputs.push({ name: tc.name, arguments: parsedArgs, id: tc.id, result });
4540
+ if (provider === "anthropic") {
4541
+ toolResultBlocks.push({ type: "tool_result", tool_use_id: tc.id, content: result });
4542
+ } else {
4543
+ messages.push(
4544
+ new Message("tool", [text(result)], { tool_call_id: tc.id, name: tc.name })
4545
+ );
4546
+ }
4547
+ }
4548
+ if (provider === "anthropic" && toolResultBlocks.length > 0) {
4549
+ messages.push(new Message("user", [], { tool_results: toolResultBlocks }));
4550
+ }
4551
+ if (parentEmit) {
4552
+ parentEmit("inputs", { tool_calls: toolInputs });
4553
+ }
4554
+ return messages;
4555
+ }
4448
4556
  async function executeAgent(prompt, inputs, options) {
4449
4557
  return traceSpan("executeAgent", async (emit) => {
4450
4558
  const agent = typeof prompt === "string" ? await traceSpan("load", async (loadEmit) => {
@@ -4465,7 +4573,32 @@ async function executeAgent(prompt, inputs, options) {
4465
4573
  const executor = getExecutor(provider);
4466
4574
  let response = await executor.execute(agent, messages);
4467
4575
  let iteration = 0;
4468
- while (hasToolCalls(response)) {
4576
+ while (true) {
4577
+ if (isAsyncIterable(response)) {
4578
+ const { toolCalls, content } = await consumeStream(agent, response);
4579
+ if (toolCalls.length === 0) {
4580
+ emit("iterations", iteration);
4581
+ emit("result", content);
4582
+ return content;
4583
+ }
4584
+ iteration++;
4585
+ if (iteration > maxIterations) {
4586
+ throw new Error(
4587
+ `Agent loop exceeded maxIterations (${maxIterations}). The model kept requesting tool calls. Increase maxIterations or check your tools.`
4588
+ );
4589
+ }
4590
+ const toolMessages2 = await traceSpan("toolCalls", async (toolEmit) => {
4591
+ toolEmit("signature", "prompty.executeAgent.toolCalls");
4592
+ toolEmit("description", `Tool call round ${iteration}`);
4593
+ const result2 = await buildToolMessagesFromCalls(toolCalls, content, tools, agent, toolEmit);
4594
+ toolEmit("result", result2.map((m) => ({ role: m.role, content: m.parts.map((p) => p.value ?? "").join(""), metadata: m.metadata })));
4595
+ return result2;
4596
+ });
4597
+ messages.push(...toolMessages2);
4598
+ response = await executor.execute(agent, messages);
4599
+ continue;
4600
+ }
4601
+ if (!hasToolCalls(response)) break;
4469
4602
  iteration++;
4470
4603
  if (iteration > maxIterations) {
4471
4604
  throw new Error(
@@ -4539,15 +4672,37 @@ function hasToolCalls(response) {
4539
4672
  if (typeof response !== "object" || response === null) return false;
4540
4673
  const r = response;
4541
4674
  const choices = r.choices;
4542
- if (!Array.isArray(choices) || choices.length === 0) return false;
4543
- const choice = choices[0];
4544
- const message = choice.message;
4545
- if (!message) return false;
4546
- const toolCalls = message.tool_calls;
4547
- return Array.isArray(toolCalls) && toolCalls.length > 0;
4675
+ if (Array.isArray(choices) && choices.length > 0) {
4676
+ const choice = choices[0];
4677
+ const message = choice.message;
4678
+ if (message) {
4679
+ const toolCalls = message.tool_calls;
4680
+ if (Array.isArray(toolCalls) && toolCalls.length > 0) return true;
4681
+ }
4682
+ }
4683
+ if (r.stop_reason === "tool_use" && Array.isArray(r.content)) {
4684
+ return r.content.some(
4685
+ (block) => block.type === "tool_use"
4686
+ );
4687
+ }
4688
+ if (r.object === "response" && Array.isArray(r.output)) {
4689
+ return r.output.some(
4690
+ (item) => item.type === "function_call"
4691
+ );
4692
+ }
4693
+ return false;
4548
4694
  }
4549
4695
  async function buildToolResultMessages(response, tools, parentEmit) {
4550
4696
  const r = response;
4697
+ if (Array.isArray(r.content) && r.stop_reason === "tool_use") {
4698
+ return buildAnthropicToolResultMessages(r, tools, parentEmit);
4699
+ }
4700
+ if (r.object === "response" && Array.isArray(r.output)) {
4701
+ return buildResponsesToolResultMessages(r, tools, parentEmit);
4702
+ }
4703
+ return buildOpenAIToolResultMessages(r, tools, parentEmit);
4704
+ }
4705
+ async function buildOpenAIToolResultMessages(r, tools, parentEmit) {
4551
4706
  const choices = r.choices;
4552
4707
  const choice = choices[0];
4553
4708
  const message = choice.message;
@@ -4599,6 +4754,109 @@ async function buildToolResultMessages(response, tools, parentEmit) {
4599
4754
  }
4600
4755
  return messages;
4601
4756
  }
4757
+ async function buildAnthropicToolResultMessages(r, tools, parentEmit) {
4758
+ const content = r.content;
4759
+ const toolUseBlocks = content.filter((block) => block.type === "tool_use");
4760
+ const messages = [];
4761
+ const textParts = content.filter((block) => block.type === "text").map((block) => text(block.text));
4762
+ messages.push(
4763
+ new Message("assistant", textParts, { content })
4764
+ );
4765
+ const toolInputs = [];
4766
+ const toolResultBlocks = [];
4767
+ for (const block of toolUseBlocks) {
4768
+ const toolName = block.name;
4769
+ const toolCallId = block.id;
4770
+ const toolArgs = block.input;
4771
+ let result;
4772
+ try {
4773
+ const toolFn = tools[toolName];
4774
+ if (!toolFn) {
4775
+ result = `Error: tool "${toolName}" not found`;
4776
+ } else {
4777
+ const toolResult = await traceSpan(toolName, async (toolEmit) => {
4778
+ toolEmit("signature", `prompty.tool.${toolName}`);
4779
+ toolEmit("description", `Execute tool: ${toolName}`);
4780
+ toolEmit("inputs", { arguments: toolArgs, tool_use_id: toolCallId });
4781
+ const r2 = await toolFn(...Array.isArray(toolArgs) ? toolArgs : [toolArgs]);
4782
+ const str = typeof r2 === "string" ? r2 : JSON.stringify(r2);
4783
+ toolEmit("result", str);
4784
+ return str;
4785
+ });
4786
+ result = toolResult;
4787
+ }
4788
+ } catch (err) {
4789
+ result = `Error: ${err instanceof Error ? err.message : String(err)}`;
4790
+ }
4791
+ toolInputs.push({ name: toolName, arguments: toolArgs, tool_use_id: toolCallId, result });
4792
+ toolResultBlocks.push({
4793
+ type: "tool_result",
4794
+ tool_use_id: toolCallId,
4795
+ content: result
4796
+ });
4797
+ }
4798
+ if (parentEmit) {
4799
+ parentEmit("inputs", { tool_calls: toolInputs });
4800
+ }
4801
+ messages.push(
4802
+ new Message("user", [], { tool_results: toolResultBlocks })
4803
+ );
4804
+ return messages;
4805
+ }
4806
+ async function buildResponsesToolResultMessages(r, tools, parentEmit) {
4807
+ const output = r.output;
4808
+ const funcCalls = output.filter((item) => item.type === "function_call");
4809
+ const messages = [];
4810
+ const toolInputs = [];
4811
+ for (const fc of funcCalls) {
4812
+ const toolName = fc.name;
4813
+ const callId = fc.call_id ?? fc.id ?? "";
4814
+ const argsStr = fc.arguments ?? "{}";
4815
+ messages.push(
4816
+ new Message("assistant", [], {
4817
+ responses_function_call: {
4818
+ type: "function_call",
4819
+ call_id: callId,
4820
+ name: toolName,
4821
+ arguments: argsStr
4822
+ }
4823
+ })
4824
+ );
4825
+ let result;
4826
+ let parsedArgs;
4827
+ try {
4828
+ parsedArgs = JSON.parse(argsStr);
4829
+ const toolFn = tools[toolName];
4830
+ if (!toolFn) {
4831
+ result = `Error: tool "${toolName}" not found`;
4832
+ } else {
4833
+ const toolResult = await traceSpan(toolName, async (toolEmit) => {
4834
+ toolEmit("signature", `prompty.tool.${toolName}`);
4835
+ toolEmit("description", `Execute tool: ${toolName}`);
4836
+ toolEmit("inputs", { arguments: parsedArgs, call_id: callId });
4837
+ const r2 = await toolFn(...Array.isArray(parsedArgs) ? parsedArgs : [parsedArgs]);
4838
+ const str = typeof r2 === "string" ? r2 : JSON.stringify(r2);
4839
+ toolEmit("result", str);
4840
+ return str;
4841
+ });
4842
+ result = toolResult;
4843
+ }
4844
+ } catch (err) {
4845
+ result = `Error: ${err instanceof Error ? err.message : String(err)}`;
4846
+ }
4847
+ toolInputs.push({ name: toolName, arguments: parsedArgs, call_id: callId, result });
4848
+ messages.push(
4849
+ new Message("tool", [text(result)], {
4850
+ tool_call_id: callId,
4851
+ name: toolName
4852
+ })
4853
+ );
4854
+ }
4855
+ if (parentEmit) {
4856
+ parentEmit("inputs", { tool_calls: toolInputs });
4857
+ }
4858
+ return messages;
4859
+ }
4602
4860
  var runAgent = executeAgent;
4603
4861
 
4604
4862
  // src/renderers/nunjucks.ts