@guava-ai/guava-sdk 0.3.0 → 0.4.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 (57) hide show
  1. package/bin/example-runner.js +16 -7
  2. package/dist/examples/credit-card-activation.js +94 -112
  3. package/dist/examples/credit-card-activation.js.map +1 -1
  4. package/dist/examples/property-insurance.js +12 -26
  5. package/dist/examples/property-insurance.js.map +1 -1
  6. package/dist/examples/scheduling-outbound.d.ts +1 -0
  7. package/dist/examples/scheduling-outbound.js +62 -0
  8. package/dist/examples/scheduling-outbound.js.map +1 -0
  9. package/dist/examples/thai-palace.js +25 -43
  10. package/dist/examples/thai-palace.js.map +1 -1
  11. package/dist/package.json +7 -5
  12. package/dist/src/action_item.d.ts +34 -12
  13. package/dist/src/action_item.js +34 -7
  14. package/dist/src/action_item.js.map +1 -1
  15. package/dist/src/call-controller.d.ts +137 -0
  16. package/dist/src/call-controller.js +433 -0
  17. package/dist/src/call-controller.js.map +1 -0
  18. package/dist/src/commands.d.ts +67 -27
  19. package/dist/src/commands.js +41 -27
  20. package/dist/src/commands.js.map +1 -1
  21. package/dist/src/events.d.ts +47 -30
  22. package/dist/src/events.js +42 -36
  23. package/dist/src/events.js.map +1 -1
  24. package/dist/src/example_data.d.ts +1 -0
  25. package/dist/src/example_data.js +33 -0
  26. package/dist/src/example_data.js.map +1 -1
  27. package/dist/src/helpers/openai.d.ts +12 -1
  28. package/dist/src/helpers/openai.js +168 -68
  29. package/dist/src/helpers/openai.js.map +1 -1
  30. package/dist/src/index.d.ts +6 -121
  31. package/dist/src/index.js +249 -483
  32. package/dist/src/index.js.map +1 -1
  33. package/dist/src/logging.d.ts +2 -1
  34. package/dist/src/logging.js +32 -7
  35. package/dist/src/logging.js.map +1 -1
  36. package/dist/src/telemetry.d.ts +23 -0
  37. package/dist/src/telemetry.js +98 -0
  38. package/dist/src/telemetry.js.map +1 -0
  39. package/dist/src/utils.d.ts +3 -0
  40. package/dist/src/utils.js +28 -0
  41. package/dist/src/utils.js.map +1 -0
  42. package/examples/biome.json +5 -0
  43. package/examples/credit-card-activation.ts +20 -26
  44. package/examples/property-insurance.ts +6 -16
  45. package/examples/scheduling-outbound.ts +80 -0
  46. package/examples/thai-palace.ts +10 -13
  47. package/package.json +7 -5
  48. package/src/action_item.ts +53 -13
  49. package/src/call-controller.ts +451 -0
  50. package/src/commands.ts +58 -42
  51. package/src/events.ts +66 -51
  52. package/src/example_data.ts +42 -0
  53. package/src/helpers/openai.ts +73 -18
  54. package/src/index.ts +81 -403
  55. package/src/logging.ts +39 -7
  56. package/src/telemetry.ts +125 -0
  57. package/src/utils.ts +32 -0
@@ -0,0 +1,125 @@
1
+ import { getDefaultLogger, type Logger } from "./logging.ts";
2
+ import { getBaseUrl, fetchOrThrow } from "./utils.ts";
3
+
4
+ const QUEUE_MAX_SIZE = 100;
5
+ const UPLOAD_INTERVAL_MS = 10_000;
6
+
7
+ const debugEnabled = ["yes", "true"].includes(
8
+ (process.env.GUAVA_DEBUG_TELEMETRY ?? "false").toLowerCase().trim(),
9
+ );
10
+ const logger: Logger = debugEnabled
11
+ ? getDefaultLogger()
12
+ : { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} };
13
+
14
+ export const TelemetryEvent = {
15
+ METHOD_CALL: "method-call",
16
+ EXCEPTION_RAISED: "exception-raised",
17
+ } as const;
18
+
19
+ export type TelemetryEvent = (typeof TelemetryEvent)[keyof typeof TelemetryEvent];
20
+
21
+ interface QueuedEvent {
22
+ timestamp_ms: number;
23
+ event_type: TelemetryEvent;
24
+ data: Record<string, unknown>;
25
+ }
26
+
27
+ export abstract class BaseTelemetryClient {
28
+ protected sdkHeaders: Record<string, string> = {};
29
+
30
+ abstract sendEvent(event: TelemetryEvent, data?: Record<string, unknown>): void;
31
+
32
+ setSdkHeaders(headers: Record<string, string>) {
33
+ this.sdkHeaders = headers;
34
+ }
35
+
36
+ trackClass(onlyExceptions = new Set<string>()) {
37
+ const client = this;
38
+ return <T extends abstract new (...args: unknown[]) => object>(
39
+ target: T,
40
+ _context: ClassDecoratorContext<T>,
41
+ ): T => {
42
+ // TODO: Wrap public methods.
43
+
44
+ // Wrap the constructor
45
+ const trackConstructorCalls = !onlyExceptions.has("constructor");
46
+ class Wrapped extends (target as unknown as new (...args: unknown[]) => object) {
47
+ constructor(...args: unknown[]) {
48
+ try {
49
+ if (trackConstructorCalls) {
50
+ client.sendEvent(TelemetryEvent.METHOD_CALL, {
51
+ function_name: `${target.name}.constructor`,
52
+ });
53
+ }
54
+ super(...args);
55
+ } catch (e) {
56
+ client.sendEvent(TelemetryEvent.EXCEPTION_RAISED, {
57
+ function_name: `${target.name}.constructor`,
58
+ exception: String(e),
59
+ });
60
+ throw e;
61
+ }
62
+ }
63
+ }
64
+ Object.defineProperty(Wrapped, "name", { value: target.name });
65
+ return Wrapped as unknown as T;
66
+ };
67
+ }
68
+ }
69
+
70
+ export class TelemetryClient extends BaseTelemetryClient {
71
+ private queue: QueuedEvent[] = [];
72
+ private timer: ReturnType<typeof setInterval>;
73
+ private readonly baseUrl: string;
74
+
75
+ constructor() {
76
+ super();
77
+ this.baseUrl = getBaseUrl();
78
+ this.timer = setInterval(() => {
79
+ void this.uploadEvents();
80
+ }, UPLOAD_INTERVAL_MS);
81
+ // Don't prevent the process from exiting naturally
82
+ this.timer.unref();
83
+
84
+ // Trigger an immediate upload on uncaught exception.
85
+ process.on("uncaughtExceptionMonitor", async () => {
86
+ await this.uploadEvents();
87
+ });
88
+ }
89
+
90
+ sendEvent(event: TelemetryEvent, data: Record<string, unknown> = {}) {
91
+ logger.debug(`Sending telemetry event ${event}, ${JSON.stringify(data)}`);
92
+ if (this.queue.length >= QUEUE_MAX_SIZE) return;
93
+ this.queue.push({ timestamp_ms: Date.now(), event_type: event, data });
94
+ }
95
+
96
+ private async uploadEvents() {
97
+ const payload = this.queue.splice(0);
98
+ if (!payload.length) {
99
+ logger.debug("No events to upload.");
100
+ return;
101
+ }
102
+ logger.debug(`Uploading ${payload.length} telemetry events.`);
103
+ try {
104
+ const url = new URL("v1/upload-telemetry", this.baseUrl);
105
+ await fetchOrThrow(url, {
106
+ method: "POST",
107
+ headers: { ...this.sdkHeaders, "Content-Type": "application/json" },
108
+ body: JSON.stringify({ events: payload }),
109
+ });
110
+ } catch (e) {
111
+ logger.error(`Telemetry upload failed: ${String(e)}`);
112
+ }
113
+ }
114
+ }
115
+
116
+ export class NoOpTelemetryClient extends BaseTelemetryClient {
117
+ sendEvent(_event: TelemetryEvent, _data?: Record<string, unknown>) {}
118
+ }
119
+
120
+ const isDisabled = ["yes", "true"].includes(
121
+ (process.env.GUAVA_DISABLE_TELEMETRY ?? "false").toLowerCase().trim(),
122
+ );
123
+ export const telemetryClient: BaseTelemetryClient = isDisabled
124
+ ? new NoOpTelemetryClient()
125
+ : new TelemetryClient();
package/src/utils.ts ADDED
@@ -0,0 +1,32 @@
1
+ export const DEFAULT_BASE_URL = "https://guava-dev.gridspace.com/";
2
+
3
+ export function getBaseUrl(): string {
4
+ return process.env.GUAVA_BASE_URL ?? DEFAULT_BASE_URL;
5
+ }
6
+
7
+ class HttpStatusError extends Error {
8
+ constructor(
9
+ public readonly status: number,
10
+ public readonly statusText: string,
11
+ public readonly body: string,
12
+ public readonly response: Response,
13
+ ) {
14
+ super(`HTTP ${status} ${statusText}${body ? ` — ${body}` : ""}`);
15
+ this.name = "HttpStatusError";
16
+ }
17
+ }
18
+
19
+ export async function fetchOrThrow(
20
+ input: RequestInfo | URL,
21
+ init?: RequestInit,
22
+ ): Promise<Response> {
23
+ // biome-ignore lint: The wrapper must call fetch.
24
+ const res = await fetch(input, init);
25
+
26
+ if (!res.ok) {
27
+ const body = await res.text().catch(() => "");
28
+ throw new HttpStatusError(res.status, res.statusText, body, res);
29
+ }
30
+
31
+ return res;
32
+ }