@letterbook/ai-chat 0.1.0 → 1.0.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.
package/README.md CHANGED
@@ -1,11 +1,13 @@
1
1
  # Letterbook AI Chat TypeScript SDK
2
2
 
3
- Capture AI chat turns from your application so conversations can appear in the Letterbook platform when customers need help.
3
+ Send AI chat turns to Letterbook so customer issues can be detected from the full conversation.
4
+
5
+ Each call captures one turn in the conversation. It does not create a ticket by itself. Letterbook queues the turns, waits for the conversation to go quiet, scans the transcript on the backend with an LLM, and creates a ticket only when it finds a relevant customer issue.
4
6
 
5
7
  ```ts
6
8
  import * as letterbook from "@letterbook/ai-chat";
7
9
 
8
- const result = await letterbook.capture({
10
+ const result = await letterbook.recordTurn({
9
11
  userId: "user123",
10
12
  event: "user_message",
11
13
  model: "gpt_4",
@@ -33,6 +35,9 @@ console.log(result.queued, result.evaluationAfterSeconds);
33
35
 
34
36
  Set `LETTERBOOK_API_KEY`, or instantiate `new letterbook.Letterbook({ apiKey })` for explicit configuration.
35
37
  Set `debounceSeconds` to control how long Letterbook waits after the last message before evaluating the conversation.
38
+ If OpenTelemetry is configured in your app, the SDK automatically propagates the active trace context with each capture request.
36
39
 
37
40
  If `customerEmail` is omitted, the SDK looks for `properties.customer_email`, `properties.email`, or an email-shaped `userId`.
38
41
  Email is only required if Letterbook promotes the capture to a ticket.
42
+
43
+ `letterbook.captureTurn(...)` and `letterbook.capture(...)` are deprecated compatibility aliases for `letterbook.recordTurn(...)`.
package/dist/index.d.ts CHANGED
@@ -12,6 +12,7 @@ export interface LetterbookOptions {
12
12
  timeoutMs?: number;
13
13
  raiseOnError?: boolean;
14
14
  fetch?: typeof fetch;
15
+ otelEnabled?: boolean;
15
16
  }
16
17
  export interface CaptureInput {
17
18
  userId: string;
@@ -51,9 +52,26 @@ export declare class Letterbook {
51
52
  private readonly timeoutMs;
52
53
  private readonly raiseOnError;
53
54
  private readonly fetchFn;
55
+ private readonly otelEnabled;
54
56
  constructor(options?: LetterbookOptions);
57
+ recordTurn(input: CaptureInput): Promise<CaptureResult>;
58
+ /**
59
+ * @deprecated Use recordTurn instead. captureTurn remains as a compatibility alias.
60
+ */
61
+ captureTurn(input: CaptureInput): Promise<CaptureResult>;
62
+ /**
63
+ * @deprecated Use recordTurn instead. capture remains as a compatibility alias.
64
+ */
55
65
  capture(input: CaptureInput): Promise<CaptureResult>;
56
66
  private postJson;
57
67
  private fail;
58
68
  }
69
+ /**
70
+ * @deprecated Use recordTurn instead. capture remains as a compatibility alias.
71
+ */
59
72
  export declare function capture(input: CaptureInput, options?: LetterbookOptions): Promise<CaptureResult>;
73
+ /**
74
+ * @deprecated Use recordTurn instead. captureTurn remains as a compatibility alias.
75
+ */
76
+ export declare function captureTurn(input: CaptureInput, options?: LetterbookOptions): Promise<CaptureResult>;
77
+ export declare function recordTurn(input: CaptureInput, options?: LetterbookOptions): Promise<CaptureResult>;
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { context, propagation } from "@opentelemetry/api";
1
2
  const DEFAULT_BASE_URL = "https://api.letterbook.ai/api";
2
3
  export class LetterbookError extends Error {
3
4
  constructor(message) {
@@ -11,22 +12,24 @@ export class Letterbook {
11
12
  timeoutMs;
12
13
  raiseOnError;
13
14
  fetchFn;
15
+ otelEnabled;
14
16
  constructor(options = {}) {
15
17
  this.apiKey = options.apiKey ?? readEnv("LETTERBOOK_API_KEY");
16
18
  this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
17
19
  this.timeoutMs = options.timeoutMs ?? 10_000;
18
20
  this.raiseOnError = options.raiseOnError ?? false;
19
21
  this.fetchFn = options.fetch ?? globalThis.fetch;
22
+ this.otelEnabled = options.otelEnabled ?? true;
20
23
  if (!this.fetchFn) {
21
24
  throw new LetterbookError("A fetch implementation is required.");
22
25
  }
23
26
  }
24
- async capture(input) {
27
+ async recordTurn(input) {
25
28
  if (input.input == null && input.output == null) {
26
- throw new LetterbookError("letterbook.capture requires at least one of input or output");
29
+ throw new LetterbookError("letterbook.recordTurn requires at least one of input or output");
27
30
  }
28
31
  if (input.debounceSeconds != null && input.debounceSeconds < 1) {
29
- throw new LetterbookError("letterbook.capture debounceSeconds must be at least 1");
32
+ throw new LetterbookError("letterbook.recordTurn debounceSeconds must be at least 1");
30
33
  }
31
34
  if (input.ticketEnabled === false) {
32
35
  return {
@@ -77,17 +80,33 @@ export class Letterbook {
77
80
  return this.fail(error instanceof Error ? error.message : String(error));
78
81
  }
79
82
  }
83
+ /**
84
+ * @deprecated Use recordTurn instead. captureTurn remains as a compatibility alias.
85
+ */
86
+ async captureTurn(input) {
87
+ return this.recordTurn(input);
88
+ }
89
+ /**
90
+ * @deprecated Use recordTurn instead. capture remains as a compatibility alias.
91
+ */
92
+ async capture(input) {
93
+ return this.recordTurn(input);
94
+ }
80
95
  async postJson(path, payload) {
81
96
  const controller = new AbortController();
82
97
  const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
83
98
  try {
99
+ const headers = {
100
+ Authorization: `Bearer ${this.apiKey}`,
101
+ "Content-Type": "application/json",
102
+ "User-Agent": "@letterbook/ai-chat/1.0.0",
103
+ };
104
+ if (this.otelEnabled) {
105
+ propagation.inject(context.active(), headers);
106
+ }
84
107
  const response = await this.fetchFn(`${this.baseUrl}${path}`, {
85
108
  method: "POST",
86
- headers: {
87
- Authorization: `Bearer ${this.apiKey}`,
88
- "Content-Type": "application/json",
89
- "User-Agent": "@letterbook/ai-chat/0.1.0",
90
- },
109
+ headers,
91
110
  body: JSON.stringify(payload),
92
111
  signal: controller.signal,
93
112
  });
@@ -112,8 +131,20 @@ export class Letterbook {
112
131
  };
113
132
  }
114
133
  }
134
+ /**
135
+ * @deprecated Use recordTurn instead. capture remains as a compatibility alias.
136
+ */
115
137
  export async function capture(input, options = {}) {
116
- return new Letterbook(options).capture(input);
138
+ return recordTurn(input, options);
139
+ }
140
+ /**
141
+ * @deprecated Use recordTurn instead. captureTurn remains as a compatibility alias.
142
+ */
143
+ export async function captureTurn(input, options = {}) {
144
+ return recordTurn(input, options);
145
+ }
146
+ export async function recordTurn(input, options = {}) {
147
+ return new Letterbook(options).recordTurn(input);
117
148
  }
118
149
  function normalizeAttachments(attachments) {
119
150
  return attachments.map((attachment) => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@letterbook/ai-chat",
3
- "version": "0.1.0",
4
- "description": "TypeScript SDK for capturing AI chat issues in Letterbook.",
3
+ "version": "1.0.0",
4
+ "description": "TypeScript SDK for sending AI chat turns to Letterbook for issue detection.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -32,5 +32,8 @@
32
32
  },
33
33
  "devDependencies": {
34
34
  "typescript": "~5.8.3"
35
+ },
36
+ "dependencies": {
37
+ "@opentelemetry/api": "^1.9.0"
35
38
  }
36
39
  }