Skip to content

Commit f21c21c

Browse files
committed
Split TypeSafe scenario into automatic and manual variants
1 parent c149d10 commit f21c21c

3 files changed

Lines changed: 93 additions & 9 deletions

File tree

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import * as Sentry from '@sentry/node';
2+
import { choice, noul, score, TypeSafeClient } from '@typesafe-ai/sdk';
3+
import express from 'express';
4+
5+
function startMockServer() {
6+
const app = express();
7+
app.use(express.json());
8+
9+
app.post('/v1/systemone', (req, res) => {
10+
if (req.body.model === 'error-model') {
11+
res.status(400).json({ error: 'Unknown model' });
12+
return;
13+
}
14+
15+
res.json({
16+
model: 'jev-1.13.0',
17+
answers: {
18+
authIssue: { type: 'noul', noul: 0.98 },
19+
department: {
20+
type: 'choice',
21+
choice: 'billing',
22+
confidence: 0.28,
23+
probabilities: { billing: 0.64, technical: 0.36 },
24+
},
25+
urgency: {
26+
type: 'score',
27+
score: 1.58,
28+
confidence: 0.9,
29+
legend: { 0: 'low', 1: 'medium', 2: 'high' },
30+
probabilities: { 0: 0, 1: 0.42, 2: 0.58 },
31+
},
32+
},
33+
usage: { input_tokens: 275, output_tokens: 20 },
34+
});
35+
});
36+
37+
return new Promise(resolve => {
38+
const server = app.listen(0, () => {
39+
resolve(server);
40+
});
41+
});
42+
}
43+
44+
async function run() {
45+
const server = await startMockServer();
46+
const client = Sentry.instrumentTypeSafeClient(
47+
new TypeSafeClient({
48+
apiKey: 'mock-api-key',
49+
baseURL: `http://localhost:${server.address().port}`,
50+
retry: { maxRetries: 0 },
51+
}),
52+
);
53+
const state = 'I cannot log in, and I also want a refund for last month.';
54+
const questions = {
55+
authIssue: noul('Is there a login problem?'),
56+
department: choice('Which team should handle this?', {
57+
billing: 'Charges and refunds',
58+
technical: 'Bugs and outages',
59+
}),
60+
urgency: score('How urgent is this ticket?', ['low', 'medium', 'high']),
61+
};
62+
63+
await Sentry.startSpan({ op: 'function', name: 'main' }, async () => {
64+
await client.systemOne({ state, questions });
65+
66+
// The instrumentation must not read the body the caller gets from `asResponse()`.
67+
const response = await client.systemOne({ model: 'jev-1.13', state, questions }).asResponse();
68+
await response.json();
69+
70+
try {
71+
await client.systemOne({ model: 'error-model', state, questions });
72+
} catch {
73+
// expected
74+
}
75+
76+
// The SDK rejects empty questions with a `TypeSafeError` before sending the request.
77+
try {
78+
await client.systemOne({ model: 'validation-error', state, questions: {} });
79+
} catch {
80+
// expected
81+
}
82+
});
83+
84+
server.close();
85+
}
86+
87+
run();

‎dev-packages/node-integration-tests/suites/tracing/typesafe/scenario.mjs‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,11 @@ function startMockServer() {
4343

4444
async function run() {
4545
const server = await startMockServer();
46-
const typesafe = new TypeSafeClient({
46+
const client = new TypeSafeClient({
4747
apiKey: 'mock-api-key',
4848
baseURL: `http://localhost:${server.address().port}`,
4949
retry: { maxRetries: 0 },
5050
});
51-
const client = Sentry.getClient().getIntegrationByName('TypeSafe')
52-
? typesafe
53-
: Sentry.instrumentTypeSafeClient(typesafe);
5451
const state = 'I cannot log in, and I also want a refund for last month.';
5552
const questions = {
5653
authIssue: noul('Is there a login problem?'),

‎dev-packages/node-integration-tests/suites/tracing/typesafe/test.ts‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ describe('TypeSafe integration', () => {
2020
});
2121

2222
describe.each([
23-
['automatic', 'instrument.mjs', 'instrument-no-recording.mjs'],
24-
['manual', 'instrument-manual.mjs', 'instrument-manual-no-recording.mjs'],
25-
])('%s instrumentation', (_, instrumentFile, noRecordingInstrumentFile) => {
23+
['automatic', 'scenario.mjs', 'instrument.mjs', 'instrument-no-recording.mjs'],
24+
['manual', 'scenario-manual.mjs', 'instrument-manual.mjs', 'instrument-manual-no-recording.mjs'],
25+
])('%s instrumentation', (_, scenarioFile, instrumentFile, noRecordingInstrumentFile) => {
2626
createEsmAndCjsTests(
2727
__dirname,
28-
'scenario.mjs',
28+
scenarioFile,
2929
instrumentFile,
3030
(createRunner, test) => {
3131
test('creates evaluate spans for systemOne', async () => {
@@ -122,7 +122,7 @@ describe('TypeSafe integration', () => {
122122

123123
createEsmAndCjsTests(
124124
__dirname,
125-
'scenario.mjs',
125+
scenarioFile,
126126
noRecordingInstrumentFile,
127127
(createRunner, test) => {
128128
test('does not record inputs or outputs when recording is off', async () => {

0 commit comments

Comments
 (0)