Skip to content
This repository was archived by the owner on Aug 5, 2025. It is now read-only.

Commit adb4d3c

Browse files
Merge pull request #54 from Chainlit/clement/eng-1746-update-sdks-to-send-the-root-run
feat: support root run
2 parents 445826f + f73840a commit adb4d3c

File tree

6 files changed

+81
-4
lines changed

6 files changed

+81
-4
lines changed

src/api.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ const version = packageJson.version;
4949
const stepFields = `
5050
id
5151
threadId
52+
rootRunId
5253
parentId
5354
startTime
5455
endTime
@@ -152,6 +153,7 @@ function ingestStepsFieldsBuilder(steps: Step[]) {
152153
for (let id = 0; id < steps.length; id++) {
153154
generated += `$id_${id}: String!
154155
$threadId_${id}: String
156+
$rootRunId_${id}: String
155157
$type_${id}: StepType
156158
$startTime_${id}: DateTime
157159
$endTime_${id}: DateTime
@@ -177,6 +179,7 @@ function ingestStepsArgsBuilder(steps: Step[]) {
177179
step${id}: ingestStep(
178180
id: $id_${id}
179181
threadId: $threadId_${id}
182+
rootRunId: $rootRunId_${id}
180183
startTime: $startTime_${id}
181184
endTime: $endTime_${id}
182185
type: $type_${id}
@@ -1651,6 +1654,7 @@ export class API {
16511654
input
16521655
expectedOutput
16531656
intermediarySteps
1657+
stepId
16541658
}
16551659
}
16561660
`;
@@ -1676,6 +1680,7 @@ export class API {
16761680
input
16771681
expectedOutput
16781682
intermediarySteps
1683+
stepId
16791684
}
16801685
}
16811686
`;
@@ -1701,6 +1706,7 @@ export class API {
17011706
input
17021707
expectedOutput
17031708
intermediarySteps
1709+
stepId
17041710
}
17051711
}
17061712
`;
@@ -1732,6 +1738,7 @@ export class API {
17321738
input
17331739
expectedOutput
17341740
intermediarySteps
1741+
stepId
17351742
}
17361743
}
17371744
`;
@@ -1767,6 +1774,7 @@ export class API {
17671774
input
17681775
expectedOutput
17691776
intermediarySteps
1777+
stepId
17701778
}
17711779
}
17721780
`;

src/evaluation/experiment-item-run.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@ export class ExperimentItemRun extends Step {
3939
{
4040
currentThread: currentStore?.currentThread ?? null,
4141
currentStep: this,
42-
currentExperimentItemRunId: this.id ?? null
42+
currentExperimentItemRunId: this.id ?? null,
43+
rootRun: currentStore?.rootRun
44+
? currentStore?.rootRun
45+
: this.type === 'run'
46+
? this
47+
: null
4348
},
4449
async () => {
4550
try {

src/index.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type StoredContext = {
1717
currentThread: Thread | null;
1818
currentStep: Step | null;
1919
currentExperimentItemRunId?: string | null;
20+
rootRun: Step | null;
2021
};
2122

2223
const storage = new AsyncLocalStorage<StoredContext>();
@@ -125,6 +126,16 @@ export class LiteralClient {
125126
return store?.currentExperimentItemRunId || null;
126127
}
127128

129+
/**
130+
* Returns the root run from the context or null if none.
131+
* @returns The root run, if any.
132+
*/
133+
_rootRun(): Step | null {
134+
const store = storage.getStore();
135+
136+
return store?.rootRun || null;
137+
}
138+
128139
/**
129140
* Gets the current thread from the context.
130141
* WARNING : this will throw if run outside of a thread context.
@@ -175,4 +186,21 @@ export class LiteralClient {
175186

176187
return store?.currentExperimentItemRunId;
177188
}
189+
190+
/**
191+
* Gets the root run from the context.
192+
* WARNING : this will throw if run outside of a step context.
193+
* @returns The current step, if any.
194+
*/
195+
getRootRun(): Step {
196+
const store = storage.getStore();
197+
198+
if (!store?.rootRun) {
199+
throw new Error(
200+
'Literal AI SDK : tried to access root run outside of a context.'
201+
);
202+
}
203+
204+
return store.rootRun;
205+
}
178206
}

src/observability/step.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class StepFields extends Utils {
2323
name!: string;
2424
type!: StepType;
2525
threadId?: string;
26+
rootRunId?: Maybe<string>;
2627
createdAt?: Maybe<string>;
2728
startTime?: Maybe<string>;
2829
id?: Maybe<string>;
@@ -73,9 +74,10 @@ export class Step extends StepFields {
7374
return;
7475
}
7576

76-
// Automatically assign parent thread & step if there are any in the store.
77+
// Automatically assign parent thread & step & rootRun if there are any in the store.
7778
this.threadId = this.threadId ?? this.client._currentThread()?.id;
7879
this.parentId = this.parentId ?? this.client._currentStep()?.id;
80+
this.rootRunId = this.rootRunId ?? this.client._rootRun()?.id;
7981

8082
// Set the creation and start time to the current time if not provided.
8183
if (!this.createdAt) {
@@ -167,7 +169,12 @@ export class Step extends StepFields {
167169
currentThread: currentStore?.currentThread ?? null,
168170
currentExperimentItemRunId:
169171
currentStore?.currentExperimentItemRunId ?? null,
170-
currentStep: this
172+
currentStep: this,
173+
rootRun: currentStore?.rootRun
174+
? currentStore?.rootRun
175+
: this.type === 'run'
176+
? this
177+
: null
171178
},
172179
() => cb(this)
173180
);
@@ -197,6 +204,7 @@ export class Step extends StepFields {
197204
this.scores = updatedStep.scores ?? this.scores;
198205
this.attachments = updatedStep.attachments ?? this.attachments;
199206
this.environment = updatedStep.environment ?? this.environment;
207+
this.rootRunId = updatedStep.rootRunId ?? this.rootRunId;
200208
}
201209

202210
this.send().catch(console.error);

src/observability/thread.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ export class Thread extends ThreadFields {
109109
currentThread: this,
110110
currentExperimentItemRunId:
111111
currentStore?.currentExperimentItemRunId ?? null,
112-
currentStep: null
112+
currentStep: null,
113+
rootRun: null
113114
},
114115
() => cb(this)
115116
);

tests/wrappers.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,32 @@ describe('Wrapper', () => {
143143
});
144144
});
145145

146+
it('handles nested runs', async () => {
147+
let runId: Maybe<string>;
148+
let stepId: Maybe<string>;
149+
150+
const step = async (_query: string) =>
151+
client.step({ name: 'foo', type: 'undefined' }).wrap(async () => {
152+
stepId = client.getCurrentStep()!.id;
153+
});
154+
155+
await client.thread({ name: 'Test Wrappers Thread' }).wrap(async () => {
156+
return client.run({ name: 'Test Wrappers Run' }).wrap(async () => {
157+
runId = client.getCurrentStep()!.id;
158+
159+
return client.run({ name: 'Test Nested Run' }).wrap(async () => {
160+
await step('foo');
161+
});
162+
});
163+
});
164+
165+
await sleep(1000);
166+
const run = await client.api.getStep(runId!);
167+
const createdStep = await client.api.getStep(stepId!);
168+
169+
expect(createdStep!.rootRunId).toEqual(run!.id);
170+
});
171+
146172
it('handles steps outside of a thread', async () => {
147173
let runId: Maybe<string>;
148174
let stepId: Maybe<string>;
@@ -172,6 +198,7 @@ describe('Wrapper', () => {
172198
expect(step!.name).toEqual('Test Wrappers Step');
173199
expect(step!.threadId).toBeNull();
174200
expect(step!.parentId).toEqual(run!.id);
201+
expect(step!.rootRunId).toEqual(run!.id);
175202
});
176203

177204
it("doesn't leak the current store when getting entities from the API", async () => {

0 commit comments

Comments
 (0)