galileo-generated 0.2.11 → 0.2.12
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 +1 -1
- package/dist/commonjs/lib/galileo-config.d.ts +11 -2
- package/dist/commonjs/lib/galileo-config.d.ts.map +1 -1
- package/dist/commonjs/lib/galileo-config.js +24 -5
- package/dist/commonjs/lib/galileo-config.js.map +1 -1
- package/dist/commonjs/models/errors/responsevalidationerror.d.ts +2 -2
- package/dist/commonjs/models/errors/responsevalidationerror.d.ts.map +1 -1
- package/dist/commonjs/models/errors/responsevalidationerror.js +9 -9
- package/dist/commonjs/models/errors/responsevalidationerror.js.map +1 -1
- package/dist/commonjs/models/errors/sdkvalidationerror.d.ts +2 -2
- package/dist/commonjs/models/errors/sdkvalidationerror.d.ts.map +1 -1
- package/dist/commonjs/models/errors/sdkvalidationerror.js +18 -10
- package/dist/commonjs/models/errors/sdkvalidationerror.js.map +1 -1
- package/dist/commonjs/tests/integration/env-auth-ingestion.test.d.ts +2 -0
- package/dist/commonjs/tests/integration/env-auth-ingestion.test.d.ts.map +1 -0
- package/dist/commonjs/tests/integration/env-auth-ingestion.test.js +185 -0
- package/dist/commonjs/tests/integration/env-auth-ingestion.test.js.map +1 -0
- package/dist/esm/lib/galileo-config.d.ts +11 -2
- package/dist/esm/lib/galileo-config.d.ts.map +1 -1
- package/dist/esm/lib/galileo-config.js +24 -5
- package/dist/esm/lib/galileo-config.js.map +1 -1
- package/dist/esm/models/errors/responsevalidationerror.d.ts +2 -2
- package/dist/esm/models/errors/responsevalidationerror.d.ts.map +1 -1
- package/dist/esm/models/errors/responsevalidationerror.js +9 -9
- package/dist/esm/models/errors/responsevalidationerror.js.map +1 -1
- package/dist/esm/models/errors/sdkvalidationerror.d.ts +2 -2
- package/dist/esm/models/errors/sdkvalidationerror.d.ts.map +1 -1
- package/dist/esm/models/errors/sdkvalidationerror.js +18 -10
- package/dist/esm/models/errors/sdkvalidationerror.js.map +1 -1
- package/dist/esm/tests/integration/env-auth-ingestion.test.d.ts +2 -0
- package/dist/esm/tests/integration/env-auth-ingestion.test.d.ts.map +1 -0
- package/dist/esm/tests/integration/env-auth-ingestion.test.js +183 -0
- package/dist/esm/tests/integration/env-auth-ingestion.test.js.map +1 -0
- package/package.json +1 -1
- package/src/lib/galileo-config.ts +26 -5
- package/src/models/errors/responsevalidationerror.ts +14 -10
- package/src/models/errors/sdkvalidationerror.ts +20 -11
- package/src/tests/integration/env-auth-ingestion.test.ts +254 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Code generated by Speakeasy (https://speakeasy.com).
|
|
3
|
-
*
|
|
2
|
+
* Code originally generated by Speakeasy (https://speakeasy.com).
|
|
3
|
+
* 03-20-2026 - Modified by Galileo to simplify the validation error message.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import * as z from "zod/v4/core";
|
|
@@ -11,12 +11,12 @@ export class ResponseValidationError extends GalileoGeneratedError {
|
|
|
11
11
|
/**
|
|
12
12
|
* The raw value that failed validation.
|
|
13
13
|
*/
|
|
14
|
-
|
|
14
|
+
protected readonly rawValue: unknown;
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* The raw message that failed validation.
|
|
18
18
|
*/
|
|
19
|
-
|
|
19
|
+
protected readonly rawMessage: unknown;
|
|
20
20
|
|
|
21
21
|
constructor(
|
|
22
22
|
message: string,
|
|
@@ -29,11 +29,19 @@ export class ResponseValidationError extends GalileoGeneratedError {
|
|
|
29
29
|
rawMessage: unknown;
|
|
30
30
|
},
|
|
31
31
|
) {
|
|
32
|
+
const causeString =
|
|
33
|
+
extra.cause instanceof z.$ZodError
|
|
34
|
+
? formatZodError(extra.cause)
|
|
35
|
+
: String(extra.cause);
|
|
36
|
+
|
|
32
37
|
super(message, extra);
|
|
33
38
|
this.name = "ResponseValidationError";
|
|
34
|
-
this.cause =
|
|
39
|
+
this.cause = causeString;
|
|
35
40
|
this.rawValue = extra.rawValue;
|
|
36
41
|
this.rawMessage = extra.rawMessage;
|
|
42
|
+
|
|
43
|
+
Object.defineProperty(this, "rawValue", { value: extra.rawValue, enumerable: false, writable: false, configurable: true });
|
|
44
|
+
Object.defineProperty(this, "rawMessage", { value: extra.rawMessage, enumerable: false, writable: false, configurable: true });
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
/**
|
|
@@ -42,10 +50,6 @@ export class ResponseValidationError extends GalileoGeneratedError {
|
|
|
42
50
|
* default error message.
|
|
43
51
|
*/
|
|
44
52
|
public pretty(): string {
|
|
45
|
-
|
|
46
|
-
return `${this.rawMessage}\n${formatZodError(this.cause)}`;
|
|
47
|
-
} else {
|
|
48
|
-
return this.toString();
|
|
49
|
-
}
|
|
53
|
+
return `${this.rawMessage}\n${this.cause}`;
|
|
50
54
|
}
|
|
51
55
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Code generated by Speakeasy (https://speakeasy.com).
|
|
3
|
-
*
|
|
2
|
+
* Code originally generated by Speakeasy (https://speakeasy.com).
|
|
3
|
+
* 03-20-2026 - Modified by Galileo to simplify the validation error message.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import * as z from "zod/v4/core";
|
|
@@ -9,12 +9,12 @@ export class SDKValidationError extends Error {
|
|
|
9
9
|
/**
|
|
10
10
|
* The raw value that failed validation.
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
protected readonly rawValue: unknown;
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* The raw message that failed validation.
|
|
16
16
|
*/
|
|
17
|
-
|
|
17
|
+
protected readonly rawMessage: unknown;
|
|
18
18
|
|
|
19
19
|
// Allows for backwards compatibility for `instanceof` checks of `ResponseValidationError`
|
|
20
20
|
static override [Symbol.hasInstance](
|
|
@@ -29,11 +29,24 @@ export class SDKValidationError extends Error {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
constructor(message: string, cause: unknown, rawValue: unknown) {
|
|
32
|
-
|
|
32
|
+
let causeDescription: string;
|
|
33
|
+
if (cause instanceof z.$ZodError) {
|
|
34
|
+
const issuesList = cause.issues;
|
|
35
|
+
const pathArray = issuesList.map((issue: z.$ZodIssue) => issue?.path?.length ? issue.path.join(".") : "").filter((path: string) => path !== "");
|
|
36
|
+
const uniquePaths = [...new Set(pathArray)];
|
|
37
|
+
const paths = uniquePaths?.length ? ` (at ${uniquePaths.join(", ")})` : "";
|
|
38
|
+
causeDescription = `${issuesList[0]?.message ?? "validation failed"}${paths}`;
|
|
39
|
+
} else {
|
|
40
|
+
causeDescription = String(cause);
|
|
41
|
+
}
|
|
42
|
+
super(message);
|
|
33
43
|
this.name = "SDKValidationError";
|
|
34
|
-
this.cause =
|
|
44
|
+
this.cause = causeDescription;
|
|
35
45
|
this.rawValue = rawValue;
|
|
36
46
|
this.rawMessage = message;
|
|
47
|
+
|
|
48
|
+
Object.defineProperty(this, "rawValue", { value: rawValue, enumerable: false, writable: false, configurable: true });
|
|
49
|
+
Object.defineProperty(this, "rawMessage", { value: message, enumerable: false, writable: false, configurable: true });
|
|
37
50
|
}
|
|
38
51
|
|
|
39
52
|
/**
|
|
@@ -42,11 +55,7 @@ export class SDKValidationError extends Error {
|
|
|
42
55
|
* default error message.
|
|
43
56
|
*/
|
|
44
57
|
public pretty(): string {
|
|
45
|
-
|
|
46
|
-
return `${this.rawMessage}\n${formatZodError(this.cause)}`;
|
|
47
|
-
} else {
|
|
48
|
-
return this.toString();
|
|
49
|
-
}
|
|
58
|
+
return `${this.message}: ${this.cause}`;
|
|
50
59
|
}
|
|
51
60
|
}
|
|
52
61
|
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { http, HttpResponse } from 'msw';
|
|
2
|
+
import { setupServer } from 'msw/node';
|
|
3
|
+
import { describe, test, expect, beforeAll, afterAll, afterEach, beforeEach } from 'vitest';
|
|
4
|
+
import { GalileoConfig } from '../../lib/galileo-config.js';
|
|
5
|
+
import { BaseEntity } from '../../entities/base-entity.js';
|
|
6
|
+
import { GalileoGenerated } from '../../sdk/sdk.js';
|
|
7
|
+
|
|
8
|
+
const TEST_HOST = 'http://localhost:8088';
|
|
9
|
+
const MOCK_API_KEY = 'test-api-key-for-env-auth';
|
|
10
|
+
const MOCK_ACCESS_TOKEN = 'mock-access-token-from-login';
|
|
11
|
+
const MOCK_PROJECT_ID = 'proj-00000000-0000-0000-0000-000000000001';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Captured request data from MSW handlers, used to assert that env vars
|
|
15
|
+
* propagate correctly through the auth and hook chain.
|
|
16
|
+
*/
|
|
17
|
+
let capturedLoginBody: { api_key?: string } | null = null;
|
|
18
|
+
let capturedTraceHeaders: Record<string, string> = {};
|
|
19
|
+
let capturedTraceBody: Record<string, unknown> | null = null;
|
|
20
|
+
|
|
21
|
+
const loginApiKeyHandler = http.post(
|
|
22
|
+
`${TEST_HOST}/login/api_key`,
|
|
23
|
+
async ({ request }) => {
|
|
24
|
+
capturedLoginBody = (await request.json()) as { api_key?: string };
|
|
25
|
+
if (capturedLoginBody?.['api_key'] !== MOCK_API_KEY) {
|
|
26
|
+
return HttpResponse.json(
|
|
27
|
+
{ detail: 'Invalid API key' },
|
|
28
|
+
{ status: 401 },
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return HttpResponse.json({
|
|
32
|
+
access_token: MOCK_ACCESS_TOKEN,
|
|
33
|
+
token_type: 'bearer',
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const traceIngestionHandler = http.post(
|
|
39
|
+
`${TEST_HOST}/projects/${MOCK_PROJECT_ID}/traces`,
|
|
40
|
+
async ({ request }) => {
|
|
41
|
+
capturedTraceHeaders = Object.fromEntries(request.headers.entries());
|
|
42
|
+
capturedTraceBody = (await request.json()) as Record<string, unknown>;
|
|
43
|
+
return HttpResponse.json({
|
|
44
|
+
project_id: MOCK_PROJECT_ID,
|
|
45
|
+
project_name: 'test-project',
|
|
46
|
+
records_count: 1,
|
|
47
|
+
traces_count: 1,
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const server = setupServer(loginApiKeyHandler, traceIngestionHandler);
|
|
53
|
+
|
|
54
|
+
const ENV_KEYS = [
|
|
55
|
+
'GALILEO_API_KEY',
|
|
56
|
+
'GALILEO_CONSOLE_URL',
|
|
57
|
+
'GALILEO_API_URL',
|
|
58
|
+
'GALILEO_USERNAME',
|
|
59
|
+
'GALILEO_PASSWORD',
|
|
60
|
+
'GALILEO_PROJECT',
|
|
61
|
+
'GALILEO_LOG_STREAM',
|
|
62
|
+
] as const;
|
|
63
|
+
|
|
64
|
+
function clearGalileoEnv(): void {
|
|
65
|
+
for (const key of ENV_KEYS) {
|
|
66
|
+
delete process.env[key];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
describe('env vars → auth → trace ingestion', () => {
|
|
71
|
+
beforeAll(() => server.listen({ onUnhandledRequest: 'warn' }));
|
|
72
|
+
afterAll(() => server.close());
|
|
73
|
+
|
|
74
|
+
beforeEach(() => {
|
|
75
|
+
capturedLoginBody = null;
|
|
76
|
+
capturedTraceHeaders = {};
|
|
77
|
+
capturedTraceBody = null;
|
|
78
|
+
GalileoConfig.reset();
|
|
79
|
+
BaseEntity.resetForTesting();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
afterEach(() => {
|
|
83
|
+
server.resetHandlers();
|
|
84
|
+
clearGalileoEnv();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('test GALILEO_API_KEY from env reaches the login endpoint', async () => {
|
|
88
|
+
process.env['GALILEO_CONSOLE_URL'] = TEST_HOST;
|
|
89
|
+
process.env['GALILEO_API_KEY'] = MOCK_API_KEY;
|
|
90
|
+
|
|
91
|
+
const token = await BaseEntity.getToken();
|
|
92
|
+
|
|
93
|
+
expect(capturedLoginBody).not.toBeNull();
|
|
94
|
+
expect(capturedLoginBody?.['api_key']).toBe(MOCK_API_KEY);
|
|
95
|
+
expect(token).toBe(MOCK_ACCESS_TOKEN);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('test auth token is injected into trace ingestion request via hook', async () => {
|
|
99
|
+
process.env['GALILEO_CONSOLE_URL'] = TEST_HOST;
|
|
100
|
+
process.env['GALILEO_API_KEY'] = MOCK_API_KEY;
|
|
101
|
+
|
|
102
|
+
// Authenticate — populates BaseEntity.token via the hook chain
|
|
103
|
+
await BaseEntity.getToken();
|
|
104
|
+
|
|
105
|
+
// Create a fresh SDK client that reads from GalileoConfig (which got TEST_HOST)
|
|
106
|
+
const client = new GalileoGenerated({ serverURL: TEST_HOST });
|
|
107
|
+
|
|
108
|
+
// Call trace ingestion through the SDK
|
|
109
|
+
await client.trace.logTracesProjectsProjectIdTracesPost(
|
|
110
|
+
{}, // security — empty: let the TokenManagementHook inject the Bearer token
|
|
111
|
+
{
|
|
112
|
+
projectId: MOCK_PROJECT_ID,
|
|
113
|
+
body: {
|
|
114
|
+
traces: [{ input: 'hello', output: 'world', name: 'test-trace' }],
|
|
115
|
+
logStreamId: 'ls-test',
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// The TokenManagementHook should have added the Bearer token
|
|
121
|
+
expect(capturedTraceHeaders['authorization']).toBe(
|
|
122
|
+
`Bearer ${MOCK_ACCESS_TOKEN}`,
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('test trace ingestion receives correct payload', async () => {
|
|
127
|
+
process.env['GALILEO_CONSOLE_URL'] = TEST_HOST;
|
|
128
|
+
process.env['GALILEO_API_KEY'] = MOCK_API_KEY;
|
|
129
|
+
|
|
130
|
+
await BaseEntity.getToken();
|
|
131
|
+
|
|
132
|
+
const client = new GalileoGenerated({ serverURL: TEST_HOST });
|
|
133
|
+
|
|
134
|
+
await client.trace.logTracesProjectsProjectIdTracesPost(
|
|
135
|
+
{},
|
|
136
|
+
{
|
|
137
|
+
projectId: MOCK_PROJECT_ID,
|
|
138
|
+
body: {
|
|
139
|
+
traces: [{ input: 'test-input', output: 'test-output', name: 'trace-1' }],
|
|
140
|
+
logStreamId: 'ls-abc',
|
|
141
|
+
isComplete: true,
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
expect(capturedTraceBody).not.toBeNull();
|
|
147
|
+
expect(capturedTraceBody?.['log_stream_id']).toBe('ls-abc');
|
|
148
|
+
expect(capturedTraceBody?.['is_complete']).toBe(true);
|
|
149
|
+
|
|
150
|
+
const traces = capturedTraceBody?.['traces'] as Array<Record<string, unknown>>;
|
|
151
|
+
expect(traces).toHaveLength(1);
|
|
152
|
+
expect(traces[0]?.['input']).toBe('test-input');
|
|
153
|
+
expect(traces[0]?.['output']).toBe('test-output');
|
|
154
|
+
expect(traces[0]?.['name']).toBe('trace-1');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test('test SDK identifier header is present on trace ingestion', async () => {
|
|
158
|
+
process.env['GALILEO_CONSOLE_URL'] = TEST_HOST;
|
|
159
|
+
process.env['GALILEO_API_KEY'] = MOCK_API_KEY;
|
|
160
|
+
|
|
161
|
+
await BaseEntity.getToken();
|
|
162
|
+
|
|
163
|
+
const client = new GalileoGenerated({ serverURL: TEST_HOST });
|
|
164
|
+
|
|
165
|
+
await client.trace.logTracesProjectsProjectIdTracesPost(
|
|
166
|
+
{},
|
|
167
|
+
{
|
|
168
|
+
projectId: MOCK_PROJECT_ID,
|
|
169
|
+
body: {
|
|
170
|
+
traces: [{ input: 'x', output: 'y', name: 'n' }],
|
|
171
|
+
logStreamId: 'ls-test',
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
expect(capturedTraceHeaders['x-galileo-sdk']).toMatch(
|
|
177
|
+
/^galileo-generated\/\d+\.\d+\.\d+$/,
|
|
178
|
+
);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test('test wrong GALILEO_API_KEY causes authentication error', async () => {
|
|
182
|
+
process.env['GALILEO_CONSOLE_URL'] = TEST_HOST;
|
|
183
|
+
process.env['GALILEO_API_KEY'] = 'wrong-api-key';
|
|
184
|
+
|
|
185
|
+
await expect(BaseEntity.getToken()).rejects.toThrow();
|
|
186
|
+
|
|
187
|
+
// Login was attempted with the wrong key
|
|
188
|
+
expect(capturedLoginBody?.['api_key']).toBe('wrong-api-key');
|
|
189
|
+
// No token was cached — trace requests would have no auth header
|
|
190
|
+
BaseEntity.resetForTesting();
|
|
191
|
+
const token = await BaseEntity.getToken().catch(() => null);
|
|
192
|
+
expect(token).toBeNull();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test('test no credentials in env means no auth header on request', async () => {
|
|
196
|
+
process.env['GALILEO_CONSOLE_URL'] = TEST_HOST;
|
|
197
|
+
// No GALILEO_API_KEY, no USERNAME/PASSWORD
|
|
198
|
+
|
|
199
|
+
const token = await BaseEntity.getToken();
|
|
200
|
+
expect(token).toBeNull();
|
|
201
|
+
|
|
202
|
+
const client = new GalileoGenerated({ serverURL: TEST_HOST });
|
|
203
|
+
|
|
204
|
+
await client.trace.logTracesProjectsProjectIdTracesPost(
|
|
205
|
+
{},
|
|
206
|
+
{
|
|
207
|
+
projectId: MOCK_PROJECT_ID,
|
|
208
|
+
body: {
|
|
209
|
+
traces: [{ input: 'a', output: 'b', name: 'c' }],
|
|
210
|
+
logStreamId: 'ls-test',
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
// TokenManagementHook skips injection when token is null
|
|
216
|
+
expect(capturedTraceHeaders['authorization']).toBeUndefined();
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test('test full chain: env var set after config reset is picked up', async () => {
|
|
220
|
+
// Simulate late env loading (e.g. dotenv.config() called after first import)
|
|
221
|
+
const configBefore = GalileoConfig.get();
|
|
222
|
+
expect(configBefore.apiKey).toBeUndefined();
|
|
223
|
+
|
|
224
|
+
// Now "load" the env vars (as dotenv would)
|
|
225
|
+
process.env['GALILEO_CONSOLE_URL'] = TEST_HOST;
|
|
226
|
+
process.env['GALILEO_API_KEY'] = MOCK_API_KEY;
|
|
227
|
+
|
|
228
|
+
// GalileoConfig.get() re-reads env when instance has no external config
|
|
229
|
+
// (isMissingExternalConfig returns true → singleton is rebuilt)
|
|
230
|
+
const configAfter = GalileoConfig.get();
|
|
231
|
+
expect(configAfter.apiKey).toBe(MOCK_API_KEY);
|
|
232
|
+
|
|
233
|
+
// Auth picks up the new config
|
|
234
|
+
const token = await BaseEntity.getToken();
|
|
235
|
+
expect(token).toBe(MOCK_ACCESS_TOKEN);
|
|
236
|
+
|
|
237
|
+
// And the token flows through to the request
|
|
238
|
+
const client = new GalileoGenerated({ serverURL: TEST_HOST });
|
|
239
|
+
await client.trace.logTracesProjectsProjectIdTracesPost(
|
|
240
|
+
{},
|
|
241
|
+
{
|
|
242
|
+
projectId: MOCK_PROJECT_ID,
|
|
243
|
+
body: {
|
|
244
|
+
traces: [{ input: 'late', output: 'load', name: 'dotenv-test' }],
|
|
245
|
+
logStreamId: 'ls-test',
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
expect(capturedTraceHeaders['authorization']).toBe(
|
|
251
|
+
`Bearer ${MOCK_ACCESS_TOKEN}`,
|
|
252
|
+
);
|
|
253
|
+
});
|
|
254
|
+
});
|