@stainless-api/docs 0.1.0-beta.135 → 0.1.0-beta.137

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.
@@ -1,126 +0,0 @@
1
- import type { DocsLanguage } from '@stainless-api/docs-ui/routing';
2
- import { JSONParser } from '@streamparser/json-whatwg';
3
- import {
4
- type RequestBody,
5
- responseChunk,
6
- type FeedbackRequestBody,
7
- feedbackResponseBody,
8
- type MetadataRequestBody,
9
- metadataResponseBody,
10
- } from '../schemas';
11
- import { streamAsyncIterator } from '../stream-util';
12
- import { DocsChatHandler } from '../docs-chat-handler';
13
-
14
- const API_URL = new URL('https://app.stainless.com/api/');
15
- const CHAT_ENDPOINT = new URL('ai/get-agentic-help', API_URL);
16
-
17
- const FEEDBACK_ENDPOINT = (spanId: string) => new URL(`ai/agentic-help/${spanId}/score`, API_URL);
18
- const METADATA_ENDPOINT = (spanId: string) => new URL(`ai/agentic-help/${spanId}/metadata`, API_URL);
19
-
20
- /**
21
- * Identifier for tracking unique users in braintrust
22
- */
23
- function getClientId() {
24
- let clientId = localStorage.getItem('stainless-client-id');
25
- if (!clientId) {
26
- clientId = crypto.randomUUID();
27
- localStorage.setItem('stainless-client-id', clientId);
28
- }
29
- return clientId;
30
- }
31
-
32
- /** Context on what the user is currently viewing to pass to the agent */
33
- function getPageContext({ siteTitle }: { siteTitle: string | undefined }) {
34
- const { href } = window.location;
35
- const markdownUrl = `${href.replace(/\/$/, '')}/index.md`;
36
- const pageTitle = document.querySelector('h1')?.textContent;
37
- return [
38
- `The user is viewing a documentation page${siteTitle ? ` for ${siteTitle}` : ''}.`,
39
- `- Content URL: ${markdownUrl}`,
40
- pageTitle && `- Page title: "${pageTitle}"`,
41
- // TODO: include stainless path here? does the agent know how to use it?
42
- // TODO: pass more of the page content into context without the agent having to retrieve it
43
- ]
44
- .filter(Boolean)
45
- .join('\n');
46
- }
47
-
48
- export class StainlessHandler implements DocsChatHandler {
49
- constructor(
50
- private language: DocsLanguage,
51
- private siteTitle: string | undefined,
52
- private project: string,
53
- ) {}
54
- /**
55
- * Stream chat response from the server
56
- */
57
- async *generateResponse(
58
- {
59
- query,
60
- priorMessages,
61
- }: {
62
- query: string;
63
- priorMessages: NonNullable<RequestBody['additionalContext']>['prior_messages'];
64
- },
65
- abortSignal: AbortSignal,
66
- ) {
67
- const res = await fetch(CHAT_ENDPOINT, {
68
- method: 'POST',
69
- headers: {
70
- 'Content-Type': 'application/json',
71
- },
72
- body: JSON.stringify({
73
- query,
74
- sdk: { project: this.project, language: this.language },
75
- stream: true,
76
- additionalContext: {
77
- prior_messages: priorMessages,
78
- intent: getPageContext({ siteTitle: this.siteTitle }),
79
- },
80
- browser_id: getClientId(),
81
- } satisfies RequestBody),
82
-
83
- signal: abortSignal,
84
- });
85
-
86
- if (!res.ok || !res.body) throw new Error(`Chat request failed with status ${res.status}`);
87
-
88
- const parser = new JSONParser({ separator: '\n', paths: ['$'] });
89
- for await (const chunk of streamAsyncIterator(res.body.pipeThrough(parser))) {
90
- const chunkParsed = responseChunk.safeParse(chunk.value);
91
- if (chunkParsed.success) yield chunkParsed.data;
92
- }
93
- }
94
-
95
- /**
96
- * Attach a score to a response
97
- */
98
- async onRate(spanId: string, score: 0 | 1) {
99
- const res = await fetch(FEEDBACK_ENDPOINT(spanId), {
100
- method: 'PUT',
101
- headers: {
102
- 'Content-Type': 'application/json',
103
- },
104
- body: JSON.stringify({ score } satisfies FeedbackRequestBody),
105
- });
106
-
107
- if (!res.ok) throw new Error(`Feedback request failed with status ${res.status}`);
108
- return feedbackResponseBody.parse(await res.json());
109
- }
110
-
111
- /**
112
- * Attach metadata to a response
113
- */
114
- async onAssignMetadata(spanId: string, metadata: Record<string, string>) {
115
- const res = await fetch(METADATA_ENDPOINT(spanId), {
116
- method: 'PUT',
117
- headers: {
118
- 'Content-Type': 'application/json',
119
- },
120
- body: JSON.stringify({ metadata } satisfies MetadataRequestBody),
121
- });
122
-
123
- if (!res.ok) throw new Error(`Metadata request failed with status ${res.status}`);
124
- return metadataResponseBody.parse(await res.json());
125
- }
126
- }
@@ -1,16 +0,0 @@
1
- /*
2
- * https://jakearchibald.com/2017/async-iterators-and-generators/#making-streams-iterate
3
- * safari does not yet support consuming ReadableStream as AsyncIterable
4
- */
5
- export async function* streamAsyncIterator<T>(stream: ReadableStream<T>) {
6
- const reader = stream.getReader();
7
- try {
8
- while (true) {
9
- const { done, value } = await reader.read();
10
- if (done) return;
11
- yield value;
12
- }
13
- } finally {
14
- reader.releaseLock();
15
- }
16
- }