@@ -29,6 +29,7 @@ import {
2929import { HumanloopSpanExporter } from "./otel/exporter" ;
3030import { HumanloopSpanProcessor } from "./otel/processor" ;
3131import { overloadCall , overloadLog } from "./overload" ;
32+ import { SyncClient , SyncClientOptions } from "./sync" ;
3233import { SDK_VERSION } from "./version" ;
3334
3435const RED = "\x1b[91m" ;
@@ -210,6 +211,7 @@ export class HumanloopClient extends BaseHumanloopClient {
210211 Anthropic ?: any ;
211212 CohereAI ?: any ;
212213 } ;
214+ protected readonly _syncClient : SyncClient ;
213215
214216 protected get opentelemetryTracer ( ) : Tracer {
215217 return HumanloopTracerSingleton . getInstance ( {
@@ -250,10 +252,13 @@ export class HumanloopClient extends BaseHumanloopClient {
250252 Anthropic ?: any ;
251253 CohereAI ?: any ;
252254 } ;
255+ sync ?: SyncClientOptions ;
253256 } ,
254257 ) {
255258 super ( _options ) ;
256259
260+ this . _syncClient = new SyncClient ( this , _options . sync ) ;
261+
257262 this . instrumentProviders = _options . instrumentProviders || { } ;
258263
259264 this . _prompts_overloaded = overloadLog ( super . prompts ) ;
@@ -560,6 +565,48 @@ ${RESET}`,
560565 ) ;
561566 }
562567
568+ /**
569+ * Pull Prompt and Agent files from Humanloop to local filesystem.
570+ *
571+ * This method will:
572+ * 1. Fetch Prompt and Agent files from your Humanloop workspace
573+ * 2. Save them to the local filesystem using the client's files_directory (set during initialization)
574+ * 3. Maintain the same directory structure as in Humanloop
575+ * 4. Add appropriate file extensions (.prompt or .agent)
576+ *
577+ * The path parameter can be used in two ways:
578+ * - If it points to a specific file (e.g. "path/to/file.prompt" or "path/to/file.agent"), only that file will be pulled
579+ * - If it points to a directory (e.g. "path/to/directory"), all Prompt and Agent files in that directory will be pulled
580+ * - If no path is provided, all Prompt and Agent files will be pulled
581+ *
582+ * The operation will overwrite existing files with the latest version from Humanloop
583+ * but will not delete local files that don't exist in the remote workspace.
584+ *
585+ * Currently only supports syncing prompt and agent files. Other file types will be skipped.
586+ *
587+ * The files will be saved with the following structure:
588+ * ```
589+ * {files_directory}/
590+ * ├── prompts/
591+ * │ ├── my_prompt.prompt
592+ * │ └── nested/
593+ * │ └── another_prompt.prompt
594+ * └── agents/
595+ * └── my_agent.agent
596+ * ```
597+ *
598+ * @param path - Optional path to either a specific file (e.g. "path/to/file.prompt") or a directory (e.g. "path/to/directory").
599+ * If not provided, all Prompt and Agent files will be pulled.
600+ * @param environment - The environment to pull the files from.
601+ * @returns List of successfully processed file paths.
602+ */
603+ public async pull (
604+ path ?: string ,
605+ environment ?: string ,
606+ ) : Promise < string [ ] > {
607+ return this . _syncClient . pull ( path , environment ) ;
608+ }
609+
563610 public get evaluations ( ) : ExtendedEvaluations {
564611 return this . _evaluations ;
565612 }
0 commit comments