eqho-eval 0.5.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 (179) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +552 -0
  3. package/dist/cli/auth-store.d.ts +5 -0
  4. package/dist/cli/auth-store.d.ts.map +1 -0
  5. package/dist/cli/auth-store.js +39 -0
  6. package/dist/cli/auth-store.js.map +1 -0
  7. package/dist/cli/banner.d.ts +3 -0
  8. package/dist/cli/banner.d.ts.map +1 -0
  9. package/dist/cli/banner.js +38 -0
  10. package/dist/cli/banner.js.map +1 -0
  11. package/dist/cli/commands/action-eval.d.ts +3 -0
  12. package/dist/cli/commands/action-eval.d.ts.map +1 -0
  13. package/dist/cli/commands/action-eval.js +133 -0
  14. package/dist/cli/commands/action-eval.js.map +1 -0
  15. package/dist/cli/commands/auth.d.ts +3 -0
  16. package/dist/cli/commands/auth.d.ts.map +1 -0
  17. package/dist/cli/commands/auth.js +156 -0
  18. package/dist/cli/commands/auth.js.map +1 -0
  19. package/dist/cli/commands/cache.d.ts +3 -0
  20. package/dist/cli/commands/cache.d.ts.map +1 -0
  21. package/dist/cli/commands/cache.js +43 -0
  22. package/dist/cli/commands/cache.js.map +1 -0
  23. package/dist/cli/commands/ci.d.ts +3 -0
  24. package/dist/cli/commands/ci.d.ts.map +1 -0
  25. package/dist/cli/commands/ci.js +124 -0
  26. package/dist/cli/commands/ci.js.map +1 -0
  27. package/dist/cli/commands/conversations.d.ts +3 -0
  28. package/dist/cli/commands/conversations.d.ts.map +1 -0
  29. package/dist/cli/commands/conversations.js +89 -0
  30. package/dist/cli/commands/conversations.js.map +1 -0
  31. package/dist/cli/commands/diff.d.ts +3 -0
  32. package/dist/cli/commands/diff.d.ts.map +1 -0
  33. package/dist/cli/commands/diff.js +122 -0
  34. package/dist/cli/commands/diff.js.map +1 -0
  35. package/dist/cli/commands/doctor.d.ts +11 -0
  36. package/dist/cli/commands/doctor.d.ts.map +1 -0
  37. package/dist/cli/commands/doctor.js +308 -0
  38. package/dist/cli/commands/doctor.js.map +1 -0
  39. package/dist/cli/commands/eval.d.ts +3 -0
  40. package/dist/cli/commands/eval.d.ts.map +1 -0
  41. package/dist/cli/commands/eval.js +101 -0
  42. package/dist/cli/commands/eval.js.map +1 -0
  43. package/dist/cli/commands/init.d.ts +3 -0
  44. package/dist/cli/commands/init.d.ts.map +1 -0
  45. package/dist/cli/commands/init.js +182 -0
  46. package/dist/cli/commands/init.js.map +1 -0
  47. package/dist/cli/commands/list.d.ts +3 -0
  48. package/dist/cli/commands/list.d.ts.map +1 -0
  49. package/dist/cli/commands/list.js +80 -0
  50. package/dist/cli/commands/list.js.map +1 -0
  51. package/dist/cli/commands/mentions.d.ts +3 -0
  52. package/dist/cli/commands/mentions.d.ts.map +1 -0
  53. package/dist/cli/commands/mentions.js +125 -0
  54. package/dist/cli/commands/mentions.js.map +1 -0
  55. package/dist/cli/commands/org.d.ts +3 -0
  56. package/dist/cli/commands/org.d.ts.map +1 -0
  57. package/dist/cli/commands/org.js +196 -0
  58. package/dist/cli/commands/org.js.map +1 -0
  59. package/dist/cli/commands/postcall-eval.d.ts +3 -0
  60. package/dist/cli/commands/postcall-eval.d.ts.map +1 -0
  61. package/dist/cli/commands/postcall-eval.js +188 -0
  62. package/dist/cli/commands/postcall-eval.js.map +1 -0
  63. package/dist/cli/commands/render.d.ts +3 -0
  64. package/dist/cli/commands/render.d.ts.map +1 -0
  65. package/dist/cli/commands/render.js +223 -0
  66. package/dist/cli/commands/render.js.map +1 -0
  67. package/dist/cli/commands/results.d.ts +3 -0
  68. package/dist/cli/commands/results.d.ts.map +1 -0
  69. package/dist/cli/commands/results.js +128 -0
  70. package/dist/cli/commands/results.js.map +1 -0
  71. package/dist/cli/commands/scenarios.d.ts +3 -0
  72. package/dist/cli/commands/scenarios.d.ts.map +1 -0
  73. package/dist/cli/commands/scenarios.js +57 -0
  74. package/dist/cli/commands/scenarios.js.map +1 -0
  75. package/dist/cli/commands/start.d.ts +3 -0
  76. package/dist/cli/commands/start.d.ts.map +1 -0
  77. package/dist/cli/commands/start.js +260 -0
  78. package/dist/cli/commands/start.js.map +1 -0
  79. package/dist/cli/commands/status.d.ts +3 -0
  80. package/dist/cli/commands/status.d.ts.map +1 -0
  81. package/dist/cli/commands/status.js +133 -0
  82. package/dist/cli/commands/status.js.map +1 -0
  83. package/dist/cli/commands/sync.d.ts +3 -0
  84. package/dist/cli/commands/sync.d.ts.map +1 -0
  85. package/dist/cli/commands/sync.js +80 -0
  86. package/dist/cli/commands/sync.js.map +1 -0
  87. package/dist/cli/commands/view.d.ts +3 -0
  88. package/dist/cli/commands/view.d.ts.map +1 -0
  89. package/dist/cli/commands/view.js +29 -0
  90. package/dist/cli/commands/view.js.map +1 -0
  91. package/dist/cli/error-handler.d.ts +8 -0
  92. package/dist/cli/error-handler.d.ts.map +1 -0
  93. package/dist/cli/error-handler.js +133 -0
  94. package/dist/cli/error-handler.js.map +1 -0
  95. package/dist/cli/gateway.d.ts +14 -0
  96. package/dist/cli/gateway.d.ts.map +1 -0
  97. package/dist/cli/gateway.js +222 -0
  98. package/dist/cli/gateway.js.map +1 -0
  99. package/dist/cli/index.d.ts +3 -0
  100. package/dist/cli/index.d.ts.map +1 -0
  101. package/dist/cli/index.js +194 -0
  102. package/dist/cli/index.js.map +1 -0
  103. package/dist/core/action-eval-builder.d.ts +20 -0
  104. package/dist/core/action-eval-builder.d.ts.map +1 -0
  105. package/dist/core/action-eval-builder.js +276 -0
  106. package/dist/core/action-eval-builder.js.map +1 -0
  107. package/dist/core/agent-fetcher.d.ts +35 -0
  108. package/dist/core/agent-fetcher.d.ts.map +1 -0
  109. package/dist/core/agent-fetcher.js +81 -0
  110. package/dist/core/agent-fetcher.js.map +1 -0
  111. package/dist/core/api-cache.d.ts +11 -0
  112. package/dist/core/api-cache.d.ts.map +1 -0
  113. package/dist/core/api-cache.js +89 -0
  114. package/dist/core/api-cache.js.map +1 -0
  115. package/dist/core/config-generator.d.ts +26 -0
  116. package/dist/core/config-generator.d.ts.map +1 -0
  117. package/dist/core/config-generator.js +457 -0
  118. package/dist/core/config-generator.js.map +1 -0
  119. package/dist/core/conversation-loader.d.ts +21 -0
  120. package/dist/core/conversation-loader.d.ts.map +1 -0
  121. package/dist/core/conversation-loader.js +74 -0
  122. package/dist/core/conversation-loader.js.map +1 -0
  123. package/dist/core/dataset-loader.d.ts +26 -0
  124. package/dist/core/dataset-loader.d.ts.map +1 -0
  125. package/dist/core/dataset-loader.js +121 -0
  126. package/dist/core/dataset-loader.js.map +1 -0
  127. package/dist/core/disposition-builder.d.ts +38 -0
  128. package/dist/core/disposition-builder.d.ts.map +1 -0
  129. package/dist/core/disposition-builder.js +270 -0
  130. package/dist/core/disposition-builder.js.map +1 -0
  131. package/dist/core/eqho-client.d.ts +45 -0
  132. package/dist/core/eqho-client.d.ts.map +1 -0
  133. package/dist/core/eqho-client.js +154 -0
  134. package/dist/core/eqho-client.js.map +1 -0
  135. package/dist/core/greeting-builder.d.ts +18 -0
  136. package/dist/core/greeting-builder.d.ts.map +1 -0
  137. package/dist/core/greeting-builder.js +83 -0
  138. package/dist/core/greeting-builder.js.map +1 -0
  139. package/dist/core/postcall-simulator.d.ts +20 -0
  140. package/dist/core/postcall-simulator.d.ts.map +1 -0
  141. package/dist/core/postcall-simulator.js +212 -0
  142. package/dist/core/postcall-simulator.js.map +1 -0
  143. package/dist/core/prompt-assembler.d.ts +25 -0
  144. package/dist/core/prompt-assembler.d.ts.map +1 -0
  145. package/dist/core/prompt-assembler.js +185 -0
  146. package/dist/core/prompt-assembler.js.map +1 -0
  147. package/dist/core/promptfoo-runner.d.ts +13 -0
  148. package/dist/core/promptfoo-runner.d.ts.map +1 -0
  149. package/dist/core/promptfoo-runner.js +49 -0
  150. package/dist/core/promptfoo-runner.js.map +1 -0
  151. package/dist/core/provider-mapper.d.ts +39 -0
  152. package/dist/core/provider-mapper.d.ts.map +1 -0
  153. package/dist/core/provider-mapper.js +120 -0
  154. package/dist/core/provider-mapper.js.map +1 -0
  155. package/dist/core/template-engine.d.ts +10 -0
  156. package/dist/core/template-engine.d.ts.map +1 -0
  157. package/dist/core/template-engine.js +78 -0
  158. package/dist/core/template-engine.js.map +1 -0
  159. package/dist/core/tools-builder.d.ts +14 -0
  160. package/dist/core/tools-builder.d.ts.map +1 -0
  161. package/dist/core/tools-builder.js +208 -0
  162. package/dist/core/tools-builder.js.map +1 -0
  163. package/dist/index.d.ts +18 -0
  164. package/dist/index.d.ts.map +1 -0
  165. package/dist/index.js +16 -0
  166. package/dist/index.js.map +1 -0
  167. package/dist/types/config.d.ts +100 -0
  168. package/dist/types/config.d.ts.map +1 -0
  169. package/dist/types/config.js +2 -0
  170. package/dist/types/config.js.map +1 -0
  171. package/dist/types/eqho.d.ts +221 -0
  172. package/dist/types/eqho.d.ts.map +1 -0
  173. package/dist/types/eqho.js +2 -0
  174. package/dist/types/eqho.js.map +1 -0
  175. package/dist/types/helpers.d.ts +9 -0
  176. package/dist/types/helpers.d.ts.map +1 -0
  177. package/dist/types/helpers.js +8 -0
  178. package/dist/types/helpers.js.map +1 -0
  179. package/package.json +77 -0
@@ -0,0 +1,121 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { parse as csvParse } from "csv-parse/sync";
4
+ /**
5
+ * Loads a dataset from CSV or JSON file and returns an array of var maps.
6
+ * CSV: each row becomes one scenario. Headers are dot-path var names
7
+ * (e.g. "lead.first_name" or "message").
8
+ * JSON: expects an array of objects with the same structure.
9
+ */
10
+ export function loadDataset(filePath) {
11
+ const resolved = path.resolve(filePath);
12
+ if (!fs.existsSync(resolved)) {
13
+ throw new Error(`Dataset file not found: ${resolved}`);
14
+ }
15
+ const ext = path.extname(resolved).toLowerCase();
16
+ const raw = fs.readFileSync(resolved, "utf-8");
17
+ if (ext === ".csv" || ext === ".tsv") {
18
+ const delimiter = ext === ".tsv" ? "\t" : ",";
19
+ const records = csvParse(raw, {
20
+ columns: true,
21
+ skip_empty_lines: true,
22
+ trim: true,
23
+ delimiter,
24
+ });
25
+ return records;
26
+ }
27
+ if (ext === ".json" || ext === ".jsonl") {
28
+ if (ext === ".jsonl") {
29
+ return raw
30
+ .split("\n")
31
+ .filter((line) => line.trim())
32
+ .map((line) => JSON.parse(line));
33
+ }
34
+ const parsed = JSON.parse(raw);
35
+ if (Array.isArray(parsed))
36
+ return parsed;
37
+ return [parsed];
38
+ }
39
+ throw new Error(`Unsupported dataset format: ${ext}. Use .csv, .tsv, .json, or .jsonl`);
40
+ }
41
+ /**
42
+ * Extracts all unique mention/variable paths found in a system prompt.
43
+ * Returns them as dot-path strings (e.g. "lead.first_name").
44
+ */
45
+ export function extractMentionPaths(systemPrompt) {
46
+ const matches = systemPrompt.match(/\{\{\s*([^}|]+?)\s*(\|[^}]*)?\s*\}\}/g) ?? [];
47
+ const paths = new Set();
48
+ for (const match of matches) {
49
+ const inner = match
50
+ .replace(/^\{\{\s*/, "")
51
+ .replace(/\s*\}\}$/, "")
52
+ .split("|")[0]
53
+ .trim();
54
+ if (inner)
55
+ paths.add(inner);
56
+ }
57
+ return [...paths].sort();
58
+ }
59
+ /**
60
+ * Generates a CSV template string with headers for all mentions.
61
+ * Includes one example row with placeholder values.
62
+ */
63
+ export function generateCsvTemplate(mentionPaths) {
64
+ const headers = ["message", ...mentionPaths];
65
+ const exampleRow = headers.map((h) => {
66
+ if (h === "message")
67
+ return "Hello, I need help with something.";
68
+ if (h.includes("first_name"))
69
+ return "Alex";
70
+ if (h.includes("last_name"))
71
+ return "Thompson";
72
+ if (h.includes("email"))
73
+ return "alex@example.com";
74
+ if (h.includes("phone"))
75
+ return "+15551234567";
76
+ if (h.includes("timezone"))
77
+ return "America/New_York";
78
+ if (h.includes("started_at"))
79
+ return new Date().toISOString();
80
+ if (h.includes("name"))
81
+ return "Example Value";
82
+ return "TODO";
83
+ });
84
+ return [headers.join(","), exampleRow.join(","), ""].join("\n");
85
+ }
86
+ /**
87
+ * Converts a dataset into promptfoo test cases.
88
+ * Each row becomes a test with vars populated from the dataset columns.
89
+ */
90
+ export function datasetToTests(rows, baseAssertions) {
91
+ return rows.map((row, i) => {
92
+ const vars = {};
93
+ for (const [key, value] of Object.entries(row)) {
94
+ if (key.startsWith("_"))
95
+ continue; // skip metadata columns
96
+ vars[key] = value;
97
+ }
98
+ const description = row._description ||
99
+ row.description ||
100
+ `Dataset scenario ${i + 1}`;
101
+ const test = {
102
+ description,
103
+ vars,
104
+ };
105
+ if (row._assert) {
106
+ try {
107
+ test.assert = JSON.parse(row._assert);
108
+ }
109
+ catch {
110
+ test.assert = [
111
+ { type: "llm-rubric", value: row._assert },
112
+ ];
113
+ }
114
+ }
115
+ else if (baseAssertions) {
116
+ test.assert = baseAssertions;
117
+ }
118
+ return test;
119
+ });
120
+ }
121
+ //# sourceMappingURL=dataset-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataset-loader.js","sourceRoot":"","sources":["../../src/core/dataset-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,KAAK,IAAI,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAMnD;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,2BAA2B,QAAQ,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IACjD,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAE/C,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;QACrC,MAAM,SAAS,GAAG,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;QAC9C,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,EAAE;YAC5B,OAAO,EAAE,IAAI;YACb,gBAAgB,EAAE,IAAI;YACtB,IAAI,EAAE,IAAI;YACV,SAAS;SACV,CAAiB,CAAC;QACnB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;QACxC,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;YACrB,OAAO,GAAG;iBACP,KAAK,CAAC,IAAI,CAAC;iBACX,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;iBAC7B,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAe,CAAC,CAAC;QACnD,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,MAAsB,CAAC;QACzD,OAAO,CAAC,MAAoB,CAAC,CAAC;IAChC,CAAC;IAED,MAAM,IAAI,KAAK,CACb,+BAA+B,GAAG,oCAAoC,CACvE,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,YAAoB;IACtD,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,uCAAuC,CAAC,IAAI,EAAE,CAAC;IAClF,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,KAAK;aAChB,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;aACvB,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;aACvB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE;aACd,IAAI,EAAE,CAAC;QACV,IAAI,KAAK;YAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,YAAsB;IACxD,MAAM,OAAO,GAAG,CAAC,SAAS,EAAE,GAAG,YAAY,CAAC,CAAC;IAC7C,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACnC,IAAI,CAAC,KAAK,SAAS;YAAE,OAAO,oCAAoC,CAAC;QACjE,IAAI,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC;YAAE,OAAO,MAAM,CAAC;QAC5C,IAAI,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;YAAE,OAAO,UAAU,CAAC;QAC/C,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,OAAO,kBAAkB,CAAC;QACnD,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,OAAO,cAAc,CAAC;QAC/C,IAAI,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;YAAE,OAAO,kBAAkB,CAAC;QACtD,IAAI,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC;YAAE,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC9D,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO,eAAe,CAAC;QAC/C,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAClE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAkB,EAClB,cAA+C;IAE/C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;QACzB,MAAM,IAAI,GAA2B,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/C,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,SAAS,CAAC,wBAAwB;YAC3D,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACpB,CAAC;QAED,MAAM,WAAW,GACf,GAAG,CAAC,YAAY;YAChB,GAAG,CAAC,WAAW;YACf,oBAAoB,CAAC,GAAG,CAAC,EAAE,CAAC;QAE9B,MAAM,IAAI,GAA4B;YACpC,WAAW;YACX,IAAI;SACL,CAAC;QAEF,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YAChB,IAAI,CAAC;gBACH,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACxC,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,CAAC,MAAM,GAAG;oBACZ,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE;iBAC3C,CAAC;YACJ,CAAC;QACH,CAAC;aAAM,IAAI,cAAc,EAAE,CAAC;YAC1B,IAAI,CAAC,MAAM,GAAG,cAAc,CAAC;QAC/B,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,38 @@
1
+ import type { EqhoDisposition, EqhoCall } from "../types/eqho.js";
2
+ import type { OpenAITool } from "../types/config.js";
3
+ import type { ProxyConfig } from "./provider-mapper.js";
4
+ /**
5
+ * Builds the set_disposition tool definition matching eqho-ai's
6
+ * ai_disposition_call() function-calling pattern.
7
+ */
8
+ export declare function buildDispositionTool(dispositions: EqhoDisposition[]): OpenAITool;
9
+ /**
10
+ * Builds the system prompt for disposition assignment, matching
11
+ * eqho-ai's disposition prompt construction.
12
+ */
13
+ export declare function buildDispositionPrompt(dispositions: EqhoDisposition[]): string;
14
+ /**
15
+ * Formats a transcript array into a readable string, stripping
16
+ * internal Action/tool-response messages that aren't part of the conversation.
17
+ */
18
+ export declare function formatTranscript(transcript: Array<{
19
+ role: string;
20
+ content: string;
21
+ }>): string;
22
+ export interface DispositionEvalConfig {
23
+ "promptfooconfig.yaml": string;
24
+ "tools/disposition-tool.json": string;
25
+ "prompts/disposition.json": string;
26
+ [key: string]: string;
27
+ }
28
+ /**
29
+ * Generates a complete promptfoo eval config for disposition testing.
30
+ * Can use real calls with known dispositions as ground truth.
31
+ */
32
+ export declare function generateDispositionEvalConfig(opts: {
33
+ dispositions: EqhoDisposition[];
34
+ campaignName: string;
35
+ calls?: EqhoCall[];
36
+ proxy?: ProxyConfig | null;
37
+ }): DispositionEvalConfig;
38
+ //# sourceMappingURL=disposition-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"disposition-builder.d.ts","sourceRoot":"","sources":["../../src/core/disposition-builder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGxD;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,eAAe,EAAE,GAC9B,UAAU,CA6BZ;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,eAAe,EAAE,GAC9B,MAAM,CAwBR;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,GACnD,MAAM,CAaR;AAED,MAAM,WAAW,qBAAqB;IACpC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,6BAA6B,EAAE,MAAM,CAAC;IACtC,0BAA0B,EAAE,MAAM,CAAC;IACnC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAClD,YAAY,EAAE,eAAe,EAAE,CAAC;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CAC5B,GAAG,qBAAqB,CAwExB"}
@@ -0,0 +1,270 @@
1
+ import yaml from "js-yaml";
2
+ import { injectProxy } from "./provider-mapper.js";
3
+ /**
4
+ * Builds the set_disposition tool definition matching eqho-ai's
5
+ * ai_disposition_call() function-calling pattern.
6
+ */
7
+ export function buildDispositionTool(dispositions) {
8
+ const enabled = dispositions.filter((d) => d.enabled !== false);
9
+ const names = enabled.map((d) => d.name);
10
+ return {
11
+ type: "function",
12
+ function: {
13
+ name: "set_disposition",
14
+ description: "Set the disposition (outcome) of the call based on the conversation transcript. " +
15
+ "Choose the most accurate disposition that reflects what happened during the call.",
16
+ parameters: {
17
+ type: "object",
18
+ properties: {
19
+ disposition: {
20
+ type: "string",
21
+ description: "The call disposition",
22
+ enum: names,
23
+ },
24
+ call_summary: {
25
+ type: "string",
26
+ description: "A concise summary of the call including key discussion points, outcomes, and any next steps.",
27
+ },
28
+ },
29
+ required: ["disposition", "call_summary"],
30
+ },
31
+ },
32
+ };
33
+ }
34
+ /**
35
+ * Builds the system prompt for disposition assignment, matching
36
+ * eqho-ai's disposition prompt construction.
37
+ */
38
+ export function buildDispositionPrompt(dispositions) {
39
+ const enabled = dispositions.filter((d) => d.enabled !== false);
40
+ const dispoList = enabled
41
+ .map((d) => {
42
+ const desc = d.description ? ` — ${d.description}` : "";
43
+ return `- ${d.name}${desc}`;
44
+ })
45
+ .join("\n");
46
+ return [
47
+ "You are a call disposition analyst. Your job is to analyze a phone call transcript and determine the most accurate disposition (outcome) for the call.",
48
+ "",
49
+ "Available dispositions:",
50
+ dispoList,
51
+ "",
52
+ "Instructions:",
53
+ "1. Read the full transcript carefully",
54
+ "2. Determine the call outcome based on what actually happened",
55
+ "3. Select the single most accurate disposition",
56
+ "4. Write a concise call summary",
57
+ "",
58
+ "Call the set_disposition function with your analysis.",
59
+ ].join("\n");
60
+ }
61
+ /**
62
+ * Formats a transcript array into a readable string, stripping
63
+ * internal Action/tool-response messages that aren't part of the conversation.
64
+ */
65
+ export function formatTranscript(transcript) {
66
+ return transcript
67
+ .filter((m) => {
68
+ if (m.role === "Action" || m.role === "action")
69
+ return false;
70
+ if (m.content?.startsWith("# TOOL-RESPONSE"))
71
+ return false;
72
+ if (m.content?.startsWith("Use the information from the result"))
73
+ return false;
74
+ return true;
75
+ })
76
+ .map((m) => {
77
+ const role = m.role === "User" || m.role === "user" ? "User" : "Agent";
78
+ return `${role}: ${m.content}`;
79
+ })
80
+ .join("\n");
81
+ }
82
+ /**
83
+ * Generates a complete promptfoo eval config for disposition testing.
84
+ * Can use real calls with known dispositions as ground truth.
85
+ */
86
+ export function generateDispositionEvalConfig(opts) {
87
+ const { dispositions, campaignName, calls, proxy } = opts;
88
+ const tool = buildDispositionTool(dispositions);
89
+ const systemPrompt = buildDispositionPrompt(dispositions);
90
+ const prompt = [
91
+ { role: "system", content: systemPrompt },
92
+ {
93
+ role: "user",
94
+ content: "Here is the call transcript:\n\n{{transcript}}",
95
+ },
96
+ ];
97
+ const callTests = calls?.length
98
+ ? buildDispositionTestsFromCalls(calls, dispositions)
99
+ : [];
100
+ const defaultTests = buildDefaultDispositionTests(dispositions);
101
+ const tests = [...callTests, ...defaultTests];
102
+ const config = {
103
+ description: `${campaignName} — Disposition Accuracy Eval`,
104
+ outputPath: [
105
+ "output/disposition-report.html",
106
+ "output/disposition-results.json",
107
+ ],
108
+ prompts: [{ id: "file://prompts/disposition.json", label: "Disposition" }],
109
+ providers: injectProxy([
110
+ {
111
+ id: "openai:chat:gpt-4.1-mini",
112
+ label: "GPT-4.1-mini",
113
+ config: {
114
+ temperature: 0,
115
+ tools: "file://tools/disposition-tool.json",
116
+ tool_choice: {
117
+ type: "function",
118
+ function: { name: "set_disposition" },
119
+ },
120
+ },
121
+ },
122
+ {
123
+ id: "openai:chat:gpt-4.1",
124
+ label: "GPT-4.1",
125
+ config: {
126
+ temperature: 0,
127
+ tools: "file://tools/disposition-tool.json",
128
+ tool_choice: {
129
+ type: "function",
130
+ function: { name: "set_disposition" },
131
+ },
132
+ },
133
+ },
134
+ ], proxy),
135
+ defaultTest: {
136
+ options: {
137
+ provider: injectProxy([{
138
+ id: "openai:chat:gpt-5-nano",
139
+ config: { temperature: 0 },
140
+ }], proxy)[0],
141
+ },
142
+ },
143
+ tests,
144
+ };
145
+ return {
146
+ "promptfooconfig.yaml": yaml.dump(config, {
147
+ lineWidth: 120,
148
+ noRefs: true,
149
+ quotingType: '"',
150
+ }),
151
+ "tools/disposition-tool.json": JSON.stringify([tool], null, 2),
152
+ "prompts/disposition.json": JSON.stringify(prompt, null, 2),
153
+ };
154
+ }
155
+ function buildDispositionTestsFromCalls(calls, dispositions) {
156
+ const tests = [];
157
+ const validNames = new Set(dispositions.filter((d) => d.enabled !== false).map((d) => d.name));
158
+ for (const call of calls) {
159
+ if (!call.transcript?.length)
160
+ continue;
161
+ const conversationMessages = call.transcript.filter((m) => m.role !== "Action" && m.role !== "action");
162
+ if (conversationMessages.length < 2)
163
+ continue;
164
+ const dispo3 = call.disposition_level_3;
165
+ const dispo2 = call.disposition_level_2;
166
+ if (!dispo3 && !dispo2)
167
+ continue;
168
+ const expectedDispo = dispo3 || dispo2;
169
+ const transcript = formatTranscript(call.transcript.map((m) => ({ role: m.role, content: m.content })));
170
+ const callId = (call.id || call._id || "unknown").slice(-6);
171
+ const summary = (call.call_summary || "").slice(0, 80);
172
+ const label = summary || call.lead?.first_name || "Real call";
173
+ const assertions = [
174
+ { type: "is-valid-openai-tools-call" },
175
+ ];
176
+ if (validNames.has(expectedDispo)) {
177
+ assertions.push({
178
+ type: "javascript",
179
+ value: `(() => { const calls = typeof output === 'string' ? JSON.parse(output) : output; const d = calls.find(c => c.function && c.function.name === 'set_disposition'); const args = typeof d.function.arguments === 'string' ? JSON.parse(d.function.arguments) : d.function.arguments; return args.disposition === '${expectedDispo.replace(/'/g, "\\'")}'; })()`,
180
+ });
181
+ }
182
+ else {
183
+ assertions.push({
184
+ type: "llm-rubric",
185
+ value: `The original system disposition was "${expectedDispo}" (a system-level status, not a campaign disposition). The model should pick the closest matching campaign disposition. A human reviewing this transcript would classify it similarly.`,
186
+ });
187
+ }
188
+ tests.push({
189
+ description: `Disposition — ${label} | expected: ${expectedDispo} [${callId}]`,
190
+ vars: { transcript },
191
+ assert: assertions,
192
+ });
193
+ }
194
+ return tests;
195
+ }
196
+ function buildDefaultDispositionTests(dispositions) {
197
+ const enabled = dispositions.filter((d) => d.enabled !== false);
198
+ const tests = [];
199
+ tests.push({
200
+ description: "Disposition — voicemail scenario",
201
+ vars: {
202
+ transcript: [
203
+ "Agent: Hi, this is an important call regarding your account.",
204
+ "Agent: It seems like I've reached your voicemail.",
205
+ "Agent: I'll try calling back at a better time. Have a great day!",
206
+ ].join("\n"),
207
+ },
208
+ assert: [
209
+ { type: "is-valid-openai-tools-call" },
210
+ {
211
+ type: "llm-rubric",
212
+ value: "The disposition should reflect that this was a voicemail or non-interactive call. A human would not categorize this as a successful contact.",
213
+ },
214
+ ],
215
+ });
216
+ tests.push({
217
+ description: "Disposition — interested lead",
218
+ vars: {
219
+ transcript: [
220
+ "Agent: Hi, this is calling about our service.",
221
+ "User: Oh yeah, I've been looking into that actually.",
222
+ "Agent: Great! Would you like to schedule a demo?",
223
+ "User: Yes, I'm free Tuesday afternoon.",
224
+ "Agent: Perfect, I'll get that set up for you.",
225
+ ].join("\n"),
226
+ },
227
+ assert: [
228
+ { type: "is-valid-openai-tools-call" },
229
+ {
230
+ type: "llm-rubric",
231
+ value: "The disposition should reflect a successful, positive outcome — the lead was interested and agreed to next steps.",
232
+ },
233
+ ],
234
+ });
235
+ tests.push({
236
+ description: "Disposition — not interested / declined",
237
+ vars: {
238
+ transcript: [
239
+ "Agent: Hi, I'm calling about our service.",
240
+ "User: No thanks, I'm not interested.",
241
+ "Agent: I understand. Have a great day!",
242
+ "User: Thanks, bye.",
243
+ ].join("\n"),
244
+ },
245
+ assert: [
246
+ { type: "is-valid-openai-tools-call" },
247
+ {
248
+ type: "llm-rubric",
249
+ value: "The disposition should reflect that the lead declined or was not interested. Should not be categorized as a positive outcome.",
250
+ },
251
+ ],
252
+ });
253
+ if (enabled.length > 0) {
254
+ tests.push({
255
+ description: "Disposition — only uses valid disposition names",
256
+ vars: {
257
+ transcript: "Agent: Hello?\nUser: Wrong number.\nAgent: Sorry about that!",
258
+ },
259
+ assert: [
260
+ { type: "is-valid-openai-tools-call" },
261
+ {
262
+ type: "javascript",
263
+ value: `(() => { const validDispos = ${JSON.stringify(enabled.map((d) => d.name))}; const calls = typeof output === 'string' ? JSON.parse(output) : output; const d = calls.find(c => c.function && c.function.name === 'set_disposition'); const args = typeof d.function.arguments === 'string' ? JSON.parse(d.function.arguments) : d.function.arguments; return validDispos.includes(args.disposition); })()`,
264
+ },
265
+ ],
266
+ });
267
+ }
268
+ return tests;
269
+ }
270
+ //# sourceMappingURL=disposition-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"disposition-builder.js","sourceRoot":"","sources":["../../src/core/disposition-builder.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,SAAS,CAAC;AAI3B,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,YAA+B;IAE/B,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAEzC,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,QAAQ,EAAE;YACR,IAAI,EAAE,iBAAiB;YACvB,WAAW,EACT,kFAAkF;gBAClF,mFAAmF;YACrF,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,WAAW,EAAE;wBACX,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,sBAAsB;wBACnC,IAAI,EAAE,KAAK;qBACZ;oBACD,YAAY,EAAE;wBACZ,IAAI,EAAE,QAAQ;wBACd,WAAW,EACT,8FAA8F;qBACjG;iBACF;gBACD,QAAQ,EAAE,CAAC,aAAa,EAAE,cAAc,CAAC;aAC1C;SACF;KACF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CACpC,YAA+B;IAE/B,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC;IAEhE,MAAM,SAAS,GAAG,OAAO;SACtB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,IAAI,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACxD,OAAO,KAAK,CAAC,CAAC,IAAI,GAAG,IAAI,EAAE,CAAC;IAC9B,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO;QACL,wJAAwJ;QACxJ,EAAE;QACF,yBAAyB;QACzB,SAAS;QACT,EAAE;QACF,eAAe;QACf,uCAAuC;QACvC,+DAA+D;QAC/D,gDAAgD;QAChD,iCAAiC;QACjC,EAAE;QACF,uDAAuD;KACxD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,UAAoD;IAEpD,OAAO,UAAU;SACd,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACZ,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QAC7D,IAAI,CAAC,CAAC,OAAO,EAAE,UAAU,CAAC,iBAAiB,CAAC;YAAE,OAAO,KAAK,CAAC;QAC3D,IAAI,CAAC,CAAC,OAAO,EAAE,UAAU,CAAC,qCAAqC,CAAC;YAAE,OAAO,KAAK,CAAC;QAC/E,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;SACD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;QACvE,OAAO,GAAG,IAAI,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;IACjC,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AASD;;;GAGG;AACH,MAAM,UAAU,6BAA6B,CAAC,IAK7C;IACC,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IAC1D,MAAM,IAAI,GAAG,oBAAoB,CAAC,YAAY,CAAC,CAAC;IAChD,MAAM,YAAY,GAAG,sBAAsB,CAAC,YAAY,CAAC,CAAC;IAE1D,MAAM,MAAM,GAAG;QACb,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE;QACzC;YACE,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,gDAAgD;SAC1D;KACF,CAAC;IAEF,MAAM,SAAS,GAAG,KAAK,EAAE,MAAM;QAC7B,CAAC,CAAC,8BAA8B,CAAC,KAAK,EAAE,YAAY,CAAC;QACrD,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,YAAY,GAAG,4BAA4B,CAAC,YAAY,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG,CAAC,GAAG,SAAS,EAAE,GAAG,YAAY,CAAC,CAAC;IAE9C,MAAM,MAAM,GAAG;QACb,WAAW,EAAE,GAAG,YAAY,8BAA8B;QAC1D,UAAU,EAAE;YACV,gCAAgC;YAChC,iCAAiC;SAClC;QACD,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,iCAAiC,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAC1E,SAAS,EAAE,WAAW,CAAC;YACrB;gBACE,EAAE,EAAE,0BAA0B;gBAC9B,KAAK,EAAE,cAAc;gBACrB,MAAM,EAAE;oBACN,WAAW,EAAE,CAAC;oBACd,KAAK,EAAE,oCAAoC;oBAC3C,WAAW,EAAE;wBACX,IAAI,EAAE,UAAU;wBAChB,QAAQ,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;qBACtC;iBACF;aACF;YACD;gBACE,EAAE,EAAE,qBAAqB;gBACzB,KAAK,EAAE,SAAS;gBAChB,MAAM,EAAE;oBACN,WAAW,EAAE,CAAC;oBACd,KAAK,EAAE,oCAAoC;oBAC3C,WAAW,EAAE;wBACX,IAAI,EAAE,UAAU;wBAChB,QAAQ,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;qBACtC;iBACF;aACF;SACF,EAAE,KAAK,CAAC;QACT,WAAW,EAAE;YACX,OAAO,EAAE;gBACP,QAAQ,EAAE,WAAW,CAAC,CAAC;wBACrB,EAAE,EAAE,wBAAwB;wBAC5B,MAAM,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE;qBAC3B,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;aACd;SACF;QACD,KAAK;KACN,CAAC;IAEF,OAAO;QACL,sBAAsB,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;YACxC,SAAS,EAAE,GAAG;YACd,MAAM,EAAE,IAAI;YACZ,WAAW,EAAE,GAAG;SACjB,CAAC;QACF,6BAA6B,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9D,0BAA0B,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;KAC5D,CAAC;AACJ,CAAC;AAED,SAAS,8BAA8B,CACrC,KAAiB,EACjB,YAA+B;IAE/B,MAAM,KAAK,GAAmC,EAAE,CAAC;IACjD,MAAM,UAAU,GAAG,IAAI,GAAG,CACxB,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CACnE,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM;YAAE,SAAS;QAEvC,MAAM,oBAAoB,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CACjD,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,CAClD,CAAC;QACF,IAAI,oBAAoB,CAAC,MAAM,GAAG,CAAC;YAAE,SAAS;QAE9C,MAAM,MAAM,GAAG,IAAI,CAAC,mBAAmB,CAAC;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,mBAAmB,CAAC;QACxC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM;YAAE,SAAS;QAEjC,MAAM,aAAa,GAAG,MAAM,IAAI,MAAO,CAAC;QACxC,MAAM,UAAU,GAAG,gBAAgB,CACjC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CACnE,CAAC;QACF,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,GAAG,IAAI,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5D,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACvD,MAAM,KAAK,GAAG,OAAO,IAAI,IAAI,CAAC,IAAI,EAAE,UAAU,IAAI,WAAW,CAAC;QAE9D,MAAM,UAAU,GAAmC;YACjD,EAAE,IAAI,EAAE,4BAA4B,EAAE;SACvC,CAAC;QAEF,IAAI,UAAU,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC;YAClC,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,YAAY;gBAClB,KAAK,EAAE,kTAAkT,aAAa,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS;aACrW,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,YAAY;gBAClB,KAAK,EAAE,wCAAwC,aAAa,wLAAwL;aACrP,CAAC,CAAC;QACL,CAAC;QAED,KAAK,CAAC,IAAI,CAAC;YACT,WAAW,EAAE,iBAAiB,KAAK,gBAAgB,aAAa,KAAK,MAAM,GAAG;YAC9E,IAAI,EAAE,EAAE,UAAU,EAAE;YACpB,MAAM,EAAE,UAAU;SACnB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,4BAA4B,CACnC,YAA+B;IAE/B,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC;IAChE,MAAM,KAAK,GAAmC,EAAE,CAAC;IAEjD,KAAK,CAAC,IAAI,CAAC;QACT,WAAW,EAAE,kCAAkC;QAC/C,IAAI,EAAE;YACJ,UAAU,EAAE;gBACV,8DAA8D;gBAC9D,mDAAmD;gBACnD,kEAAkE;aACnE,CAAC,IAAI,CAAC,IAAI,CAAC;SACb;QACD,MAAM,EAAE;YACN,EAAE,IAAI,EAAE,4BAA4B,EAAE;YACtC;gBACE,IAAI,EAAE,YAAY;gBAClB,KAAK,EACH,8IAA8I;aACjJ;SACF;KACF,CAAC,CAAC;IAEH,KAAK,CAAC,IAAI,CAAC;QACT,WAAW,EAAE,+BAA+B;QAC5C,IAAI,EAAE;YACJ,UAAU,EAAE;gBACV,+CAA+C;gBAC/C,sDAAsD;gBACtD,kDAAkD;gBAClD,wCAAwC;gBACxC,+CAA+C;aAChD,CAAC,IAAI,CAAC,IAAI,CAAC;SACb;QACD,MAAM,EAAE;YACN,EAAE,IAAI,EAAE,4BAA4B,EAAE;YACtC;gBACE,IAAI,EAAE,YAAY;gBAClB,KAAK,EACH,mHAAmH;aACtH;SACF;KACF,CAAC,CAAC;IAEH,KAAK,CAAC,IAAI,CAAC;QACT,WAAW,EAAE,yCAAyC;QACtD,IAAI,EAAE;YACJ,UAAU,EAAE;gBACV,2CAA2C;gBAC3C,sCAAsC;gBACtC,wCAAwC;gBACxC,oBAAoB;aACrB,CAAC,IAAI,CAAC,IAAI,CAAC;SACb;QACD,MAAM,EAAE;YACN,EAAE,IAAI,EAAE,4BAA4B,EAAE;YACtC;gBACE,IAAI,EAAE,YAAY;gBAClB,KAAK,EACH,+HAA+H;aAClI;SACF;KACF,CAAC,CAAC;IAEH,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC;YACT,WAAW,EAAE,iDAAiD;YAC9D,IAAI,EAAE;gBACJ,UAAU,EACR,8DAA8D;aACjE;YACD,MAAM,EAAE;gBACN,EAAE,IAAI,EAAE,4BAA4B,EAAE;gBACtC;oBACE,IAAI,EAAE,YAAY;oBAClB,KAAK,EAAE,gCAAgC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,gUAAgU;iBAClZ;aACF;SACF,CAAC,CAAC;IACL,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,45 @@
1
+ import type { EqhoCampaign, EqhoAgent, EqhoAgentDetails, EqhoRole, EqhoAction, EqhoScript, EqhoCall, EqhoMention } from "../types/eqho.js";
2
+ export declare class EqhoApiError extends Error {
3
+ status?: number | undefined;
4
+ endpoint?: string | undefined;
5
+ constructor(message: string, status?: number | undefined, endpoint?: string | undefined);
6
+ }
7
+ export interface EqhoClientOptions {
8
+ apiKey?: string;
9
+ baseUrl?: string;
10
+ apiBaseUrl?: string;
11
+ orgId?: string;
12
+ /** When set, routes through the backend proxy using this JWT */
13
+ backendUrl?: string;
14
+ backendToken?: string;
15
+ authMode?: "api_key" | "bearer";
16
+ }
17
+ export declare class EqhoClient {
18
+ private options;
19
+ private http;
20
+ constructor(options: EqhoClientOptions);
21
+ private withRetry;
22
+ listCampaigns(limit?: number, offset?: number): Promise<{
23
+ campaigns: EqhoCampaign[];
24
+ }>;
25
+ getCampaign(id: string): Promise<EqhoCampaign>;
26
+ getAgent(id: string): Promise<EqhoAgent>;
27
+ getAgentDetails(id: string): Promise<EqhoAgentDetails>;
28
+ listAgents(limit?: number, offset?: number): Promise<{
29
+ agents: EqhoAgent[];
30
+ }>;
31
+ getRole(id: string): Promise<EqhoRole>;
32
+ getAction(id: string): Promise<EqhoAction>;
33
+ getScript(id: string): Promise<EqhoScript>;
34
+ listCalls(campaignId?: string, options?: {
35
+ limit?: number;
36
+ offset?: number;
37
+ status?: string;
38
+ }): Promise<{
39
+ calls: EqhoCall[];
40
+ }>;
41
+ getCall(id: string): Promise<EqhoCall>;
42
+ listMentions(): Promise<EqhoMention[]>;
43
+ validateConnection(): Promise<boolean>;
44
+ }
45
+ //# sourceMappingURL=eqho-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eqho-client.d.ts","sourceRoot":"","sources":["../../src/core/eqho-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,QAAQ,EACR,UAAU,EACV,UAAU,EACV,QAAQ,EACR,WAAW,EACZ,MAAM,kBAAkB,CAAC;AAM1B,qBAAa,YAAa,SAAQ,KAAK;IAG5B,MAAM,CAAC,EAAE,MAAM;IACf,QAAQ,CAAC,EAAE,MAAM;gBAFxB,OAAO,EAAE,MAAM,EACR,MAAM,CAAC,EAAE,MAAM,YAAA,EACf,QAAQ,CAAC,EAAE,MAAM,YAAA;CAK3B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;CACjC;AAED,qBAAa,UAAU;IAGT,OAAO,CAAC,OAAO;IAF3B,OAAO,CAAC,IAAI,CAAgB;gBAER,OAAO,EAAE,iBAAiB;YAwBhC,SAAS;IAuCjB,aAAa,CACjB,KAAK,SAAK,EACV,MAAM,SAAI,GACT,OAAO,CAAC;QAAE,SAAS,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;IASnC,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAO9C,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAOxC,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAOtD,UAAU,CACd,KAAK,SAAK,EACV,MAAM,SAAI,GACT,OAAO,CAAC;QAAE,MAAM,EAAE,SAAS,EAAE,CAAA;KAAE,CAAC;IAS7B,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAOtC,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAO1C,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAO1C,SAAS,CACb,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO,GACjE,OAAO,CAAC;QAAE,KAAK,EAAE,QAAQ,EAAE,CAAA;KAAE,CAAC;IAa3B,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAOtC,YAAY,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAOtC,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC;CAQ7C"}
@@ -0,0 +1,154 @@
1
+ import axios from "axios";
2
+ const DEFAULT_API_URL = "https://api.eqho.ai";
3
+ const DEFAULT_RETRIES = 2;
4
+ const RETRY_BACKOFF_MS = 1000;
5
+ export class EqhoApiError extends Error {
6
+ status;
7
+ endpoint;
8
+ constructor(message, status, endpoint) {
9
+ super(message);
10
+ this.status = status;
11
+ this.endpoint = endpoint;
12
+ this.name = "EqhoApiError";
13
+ }
14
+ }
15
+ export class EqhoClient {
16
+ options;
17
+ http;
18
+ constructor(options) {
19
+ this.options = options;
20
+ const useProxy = options.authMode === "bearer" && options.backendUrl && options.backendToken;
21
+ const headers = {
22
+ "Content-Type": "application/json",
23
+ };
24
+ if (useProxy) {
25
+ headers["Authorization"] = `Bearer ${options.backendToken}`;
26
+ }
27
+ else if (options.apiKey) {
28
+ headers["X-API-KEY"] = options.apiKey;
29
+ }
30
+ if (options.orgId && !useProxy) {
31
+ headers["X-Account-Id"] = options.orgId;
32
+ }
33
+ const baseURL = useProxy
34
+ ? `${options.backendUrl}/api/eqho`
35
+ : (options.baseUrl || options.apiBaseUrl || DEFAULT_API_URL);
36
+ this.http = axios.create({ baseURL, headers, timeout: 30_000 });
37
+ }
38
+ async withRetry(fn, endpoint, retries = DEFAULT_RETRIES) {
39
+ for (let attempt = 0; attempt <= retries; attempt++) {
40
+ try {
41
+ return await fn();
42
+ }
43
+ catch (err) {
44
+ const axErr = err;
45
+ const status = axErr.response?.status;
46
+ const isRetryable = !status || status >= 500 || status === 429;
47
+ if (attempt === retries || !isRetryable) {
48
+ let msg = axErr.message;
49
+ const respData = axErr.response?.data;
50
+ if (respData && typeof respData === "object") {
51
+ const detail = respData.detail;
52
+ if (typeof detail === "string") {
53
+ msg = detail;
54
+ }
55
+ else if (detail != null) {
56
+ msg = JSON.stringify(detail);
57
+ }
58
+ }
59
+ throw new EqhoApiError(`${endpoint}: ${msg}`, status, endpoint);
60
+ }
61
+ await new Promise((r) => setTimeout(r, RETRY_BACKOFF_MS * (attempt + 1)));
62
+ }
63
+ }
64
+ throw new EqhoApiError(`${endpoint}: max retries exceeded`, undefined, endpoint);
65
+ }
66
+ async listCampaigns(limit = 50, offset = 1) {
67
+ return this.withRetry(async () => {
68
+ const { data } = await this.http.get("/v1/campaigns", {
69
+ params: { limit, offset },
70
+ });
71
+ return data;
72
+ }, "GET /v1/campaigns");
73
+ }
74
+ async getCampaign(id) {
75
+ return this.withRetry(async () => {
76
+ const { data } = await this.http.get(`/v1/campaigns/${id}`);
77
+ return data;
78
+ }, `GET /v1/campaigns/${id}`);
79
+ }
80
+ async getAgent(id) {
81
+ return this.withRetry(async () => {
82
+ const { data } = await this.http.get(`/v1/agents/${id}`);
83
+ return data;
84
+ }, `GET /v1/agents/${id}`);
85
+ }
86
+ async getAgentDetails(id) {
87
+ return this.withRetry(async () => {
88
+ const { data } = await this.http.get(`/v1/agents/${id}/details`);
89
+ return data;
90
+ }, `GET /v1/agents/${id}/details`);
91
+ }
92
+ async listAgents(limit = 50, offset = 1) {
93
+ return this.withRetry(async () => {
94
+ const { data } = await this.http.get("/v1/agents", {
95
+ params: { limit, offset },
96
+ });
97
+ return data;
98
+ }, "GET /v1/agents");
99
+ }
100
+ async getRole(id) {
101
+ return this.withRetry(async () => {
102
+ const { data } = await this.http.get(`/v1/roles/${id}`);
103
+ return data;
104
+ }, `GET /v1/roles/${id}`);
105
+ }
106
+ async getAction(id) {
107
+ return this.withRetry(async () => {
108
+ const { data } = await this.http.get(`/v1/actions/${id}`);
109
+ return data;
110
+ }, `GET /v1/actions/${id}`);
111
+ }
112
+ async getScript(id) {
113
+ return this.withRetry(async () => {
114
+ const { data } = await this.http.get(`/v1/scripts/${id}`);
115
+ return data;
116
+ }, `GET /v1/scripts/${id}`);
117
+ }
118
+ async listCalls(campaignId, options = {}) {
119
+ return this.withRetry(async () => {
120
+ const params = {
121
+ limit: options.limit || 25,
122
+ offset: options.offset || 1,
123
+ };
124
+ if (campaignId)
125
+ params.campaign_id = campaignId;
126
+ if (options.status)
127
+ params.status = options.status;
128
+ const { data } = await this.http.get("/v1/calls", { params });
129
+ return data;
130
+ }, "GET /v1/calls");
131
+ }
132
+ async getCall(id) {
133
+ return this.withRetry(async () => {
134
+ const { data } = await this.http.get(`/v1/calls/${id}`);
135
+ return data;
136
+ }, `GET /v1/calls/${id}`);
137
+ }
138
+ async listMentions() {
139
+ return this.withRetry(async () => {
140
+ const { data } = await this.http.get("/v1/mentions");
141
+ return data.mentions ?? data.data ?? data;
142
+ }, "GET /v1/mentions");
143
+ }
144
+ async validateConnection() {
145
+ try {
146
+ await this.http.get("/v1/campaigns", { params: { limit: 1 } });
147
+ return true;
148
+ }
149
+ catch {
150
+ return false;
151
+ }
152
+ }
153
+ }
154
+ //# sourceMappingURL=eqho-client.js.map