11import { JSONHeroPath } from "@jsonhero/path" ;
22import { dequal } from "dequal/lite" ;
33import { DeserializedJson } from "../../schemas/json.js" ;
4- import { apiClientManager } from "../apiClientManager-api.js" ;
5- import { taskContext } from "../task-context-api.js" ;
64import { ApiRequestOptions } from "../zodfetch.js" ;
75import { RunMetadataManager } from "./types.js" ;
86import { MetadataStream } from "./metadataStream.js" ;
7+ import { ApiClient } from "../apiClient/index.js" ;
98
109export class StandardMetadataManager implements RunMetadataManager {
1110 private flushTimeoutId : NodeJS . Timeout | null = null ;
@@ -14,7 +13,12 @@ export class StandardMetadataManager implements RunMetadataManager {
1413 // Add a Map to track active streams
1514 private activeStreams = new Map < string , MetadataStream < any > > ( ) ;
1615
17- constructor ( private streamsBaseUrl : string ) { }
16+ public runId : string | undefined ;
17+
18+ constructor (
19+ private apiClient : ApiClient ,
20+ private streamsBaseUrl : string
21+ ) { }
1822
1923 public enterWithMetadata ( metadata : Record < string , DeserializedJson > ) : void {
2024 this . store = metadata ?? { } ;
@@ -29,9 +33,7 @@ export class StandardMetadataManager implements RunMetadataManager {
2933 }
3034
3135 public setKey ( key : string , value : DeserializedJson ) {
32- const runId = taskContext . ctx ?. run . id ;
33-
34- if ( ! runId ) {
36+ if ( ! this . runId ) {
3537 return ;
3638 }
3739
@@ -61,9 +63,7 @@ export class StandardMetadataManager implements RunMetadataManager {
6163 }
6264
6365 public deleteKey ( key : string ) {
64- const runId = taskContext . ctx ?. run . id ;
65-
66- if ( ! runId ) {
66+ if ( ! this . runId ) {
6767 return ;
6868 }
6969
@@ -77,10 +77,81 @@ export class StandardMetadataManager implements RunMetadataManager {
7777 this . store = nextStore ;
7878 }
7979
80- public update ( metadata : Record < string , DeserializedJson > ) : void {
81- const runId = taskContext . ctx ?. run . id ;
80+ public appendKey ( key : string , value : DeserializedJson ) {
81+ if ( ! this . runId ) {
82+ return ;
83+ }
84+
85+ let nextStore : Record < string , DeserializedJson > | undefined = this . store
86+ ? structuredClone ( this . store )
87+ : { } ;
88+
89+ if ( key . startsWith ( "$." ) ) {
90+ const path = new JSONHeroPath ( key ) ;
91+ const currentValue = path . first ( nextStore ) ;
92+
93+ if ( currentValue === undefined ) {
94+ // Initialize as array with single item
95+ path . set ( nextStore , [ value ] ) ;
96+ } else if ( Array . isArray ( currentValue ) ) {
97+ // Append to existing array
98+ path . set ( nextStore , [ ...currentValue , value ] ) ;
99+ } else {
100+ // Convert to array if not already
101+ path . set ( nextStore , [ currentValue , value ] ) ;
102+ }
103+ } else {
104+ const currentValue = nextStore [ key ] ;
105+
106+ if ( currentValue === undefined ) {
107+ // Initialize as array with single item
108+ nextStore [ key ] = [ value ] ;
109+ } else if ( Array . isArray ( currentValue ) ) {
110+ // Append to existing array
111+ nextStore [ key ] = [ ...currentValue , value ] ;
112+ } else {
113+ // Convert to array if not already
114+ nextStore [ key ] = [ currentValue , value ] ;
115+ }
116+ }
82117
83- if ( ! runId ) {
118+ if ( ! dequal ( this . store , nextStore ) ) {
119+ this . hasChanges = true ;
120+ }
121+
122+ this . store = nextStore ;
123+ }
124+
125+ public incrementKey ( key : string , increment : number = 1 ) {
126+ if ( ! this . runId ) {
127+ return ;
128+ }
129+
130+ let nextStore = this . store ? structuredClone ( this . store ) : { } ;
131+ let currentValue = key . startsWith ( "$." )
132+ ? new JSONHeroPath ( key ) . first ( nextStore )
133+ : nextStore [ key ] ;
134+
135+ const newValue = ( typeof currentValue === "number" ? currentValue : 0 ) + increment ;
136+
137+ if ( key . startsWith ( "$." ) ) {
138+ new JSONHeroPath ( key ) . set ( nextStore , newValue ) ;
139+ } else {
140+ nextStore [ key ] = newValue ;
141+ }
142+
143+ if ( ! dequal ( this . store , nextStore ) ) {
144+ this . hasChanges = true ;
145+ this . store = nextStore ;
146+ }
147+ }
148+
149+ public decrementKey ( key : string , decrement : number = 1 ) {
150+ this . incrementKey ( key , - decrement ) ;
151+ }
152+
153+ public update ( metadata : Record < string , DeserializedJson > ) : void {
154+ if ( ! this . runId ) {
84155 return ;
85156 }
86157
@@ -96,9 +167,7 @@ export class StandardMetadataManager implements RunMetadataManager {
96167 value : AsyncIterable < T > ,
97168 signal ?: AbortSignal
98169 ) : Promise < AsyncIterable < T > > {
99- const runId = taskContext . ctx ?. run . id ;
100-
101- if ( ! runId ) {
170+ if ( ! this . runId ) {
102171 return value ;
103172 }
104173
@@ -109,7 +178,7 @@ export class StandardMetadataManager implements RunMetadataManager {
109178
110179 const streamInstance = new MetadataStream ( {
111180 key,
112- runId,
181+ runId : this . runId ,
113182 iterator : value [ Symbol . asyncIterator ] ( ) ,
114183 baseUrl : this . streamsBaseUrl ,
115184 signal,
@@ -153,9 +222,7 @@ export class StandardMetadataManager implements RunMetadataManager {
153222 }
154223
155224 public async flush ( requestOptions ?: ApiRequestOptions ) : Promise < void > {
156- const runId = taskContext . ctx ?. run . id ;
157-
158- if ( ! runId ) {
225+ if ( ! this . runId ) {
159226 return ;
160227 }
161228
@@ -167,11 +234,9 @@ export class StandardMetadataManager implements RunMetadataManager {
167234 return ;
168235 }
169236
170- const apiClient = apiClientManager . clientOrThrow ( ) ;
171-
172237 try {
173238 this . hasChanges = false ;
174- await apiClient . updateRunMetadata ( runId , { metadata : this . store } , requestOptions ) ;
239+ await this . apiClient . updateRunMetadata ( this . runId , { metadata : this . store } , requestOptions ) ;
175240 } catch ( error ) {
176241 this . hasChanges = true ;
177242 throw error ;
0 commit comments