client.cjs 141 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427
  1. "use strict";
  2. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
  3. if (k2 === undefined) k2 = k;
  4. var desc = Object.getOwnPropertyDescriptor(m, k);
  5. if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
  6. desc = { enumerable: true, get: function() { return m[k]; } };
  7. }
  8. Object.defineProperty(o, k2, desc);
  9. }) : (function(o, m, k, k2) {
  10. if (k2 === undefined) k2 = k;
  11. o[k2] = m[k];
  12. }));
  13. var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
  14. Object.defineProperty(o, "default", { enumerable: true, value: v });
  15. }) : function(o, v) {
  16. o["default"] = v;
  17. });
  18. var __importStar = (this && this.__importStar) || (function () {
  19. var ownKeys = function(o) {
  20. ownKeys = Object.getOwnPropertyNames || function (o) {
  21. var ar = [];
  22. for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
  23. return ar;
  24. };
  25. return ownKeys(o);
  26. };
  27. return function (mod) {
  28. if (mod && mod.__esModule) return mod;
  29. var result = {};
  30. if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
  31. __setModuleDefault(result, mod);
  32. return result;
  33. };
  34. })();
  35. Object.defineProperty(exports, "__esModule", { value: true });
  36. exports.Client = exports.DEFAULT_BATCH_SIZE_LIMIT_BYTES = exports.AutoBatchQueue = void 0;
  37. exports.mergeRuntimeEnvIntoRunCreate = mergeRuntimeEnvIntoRunCreate;
  38. const uuid = __importStar(require("uuid"));
  39. const async_caller_js_1 = require("./utils/async_caller.cjs");
  40. const messages_js_1 = require("./utils/messages.cjs");
  41. const env_js_1 = require("./utils/env.cjs");
  42. const index_js_1 = require("./index.cjs");
  43. const _uuid_js_1 = require("./utils/_uuid.cjs");
  44. const warn_js_1 = require("./utils/warn.cjs");
  45. const prompts_js_1 = require("./utils/prompts.cjs");
  46. const error_js_1 = require("./utils/error.cjs");
  47. const fetch_js_1 = require("./singletons/fetch.cjs");
  48. const index_js_2 = require("./utils/fast-safe-stringify/index.cjs");
  49. function mergeRuntimeEnvIntoRunCreate(run) {
  50. const runtimeEnv = (0, env_js_1.getRuntimeEnvironment)();
  51. const envVars = (0, env_js_1.getLangChainEnvVarsMetadata)();
  52. const extra = run.extra ?? {};
  53. const metadata = extra.metadata;
  54. run.extra = {
  55. ...extra,
  56. runtime: {
  57. ...runtimeEnv,
  58. ...extra?.runtime,
  59. },
  60. metadata: {
  61. ...envVars,
  62. ...(envVars.revision_id || run.revision_id
  63. ? { revision_id: run.revision_id ?? envVars.revision_id }
  64. : {}),
  65. ...metadata,
  66. },
  67. };
  68. return run;
  69. }
  70. const getTracingSamplingRate = (configRate) => {
  71. const samplingRateStr = configRate?.toString() ??
  72. (0, env_js_1.getLangSmithEnvironmentVariable)("TRACING_SAMPLING_RATE");
  73. if (samplingRateStr === undefined) {
  74. return undefined;
  75. }
  76. const samplingRate = parseFloat(samplingRateStr);
  77. if (samplingRate < 0 || samplingRate > 1) {
  78. throw new Error(`LANGSMITH_TRACING_SAMPLING_RATE must be between 0 and 1 if set. Got: ${samplingRate}`);
  79. }
  80. return samplingRate;
  81. };
  82. // utility functions
  83. const isLocalhost = (url) => {
  84. const strippedUrl = url.replace("http://", "").replace("https://", "");
  85. const hostname = strippedUrl.split("/")[0].split(":")[0];
  86. return (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1");
  87. };
  88. async function toArray(iterable) {
  89. const result = [];
  90. for await (const item of iterable) {
  91. result.push(item);
  92. }
  93. return result;
  94. }
  95. function trimQuotes(str) {
  96. if (str === undefined) {
  97. return undefined;
  98. }
  99. return str
  100. .trim()
  101. .replace(/^"(.*)"$/, "$1")
  102. .replace(/^'(.*)'$/, "$1");
  103. }
  104. const handle429 = async (response) => {
  105. if (response?.status === 429) {
  106. const retryAfter = parseInt(response.headers.get("retry-after") ?? "30", 10) * 1000;
  107. if (retryAfter > 0) {
  108. await new Promise((resolve) => setTimeout(resolve, retryAfter));
  109. // Return directly after calling this check
  110. return true;
  111. }
  112. }
  113. // Fall back to existing status checks
  114. return false;
  115. };
  116. function _formatFeedbackScore(score) {
  117. if (typeof score === "number") {
  118. // Truncate at 4 decimal places
  119. return Number(score.toFixed(4));
  120. }
  121. return score;
  122. }
  123. class AutoBatchQueue {
  124. constructor() {
  125. Object.defineProperty(this, "items", {
  126. enumerable: true,
  127. configurable: true,
  128. writable: true,
  129. value: []
  130. });
  131. Object.defineProperty(this, "sizeBytes", {
  132. enumerable: true,
  133. configurable: true,
  134. writable: true,
  135. value: 0
  136. });
  137. }
  138. peek() {
  139. return this.items[0];
  140. }
  141. push(item) {
  142. let itemPromiseResolve;
  143. const itemPromise = new Promise((resolve) => {
  144. // Setting itemPromiseResolve is synchronous with promise creation:
  145. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise
  146. itemPromiseResolve = resolve;
  147. });
  148. const size = (0, index_js_2.serialize)(item.item, `Serializing run with id: ${item.item.id}`).length;
  149. this.items.push({
  150. action: item.action,
  151. payload: item.item,
  152. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  153. itemPromiseResolve: itemPromiseResolve,
  154. itemPromise,
  155. size,
  156. });
  157. this.sizeBytes += size;
  158. return itemPromise;
  159. }
  160. pop(upToSizeBytes) {
  161. if (upToSizeBytes < 1) {
  162. throw new Error("Number of bytes to pop off may not be less than 1.");
  163. }
  164. const popped = [];
  165. let poppedSizeBytes = 0;
  166. // Pop items until we reach or exceed the size limit
  167. while (poppedSizeBytes + (this.peek()?.size ?? 0) < upToSizeBytes &&
  168. this.items.length > 0) {
  169. const item = this.items.shift();
  170. if (item) {
  171. popped.push(item);
  172. poppedSizeBytes += item.size;
  173. this.sizeBytes -= item.size;
  174. }
  175. }
  176. // If there is an item on the queue we were unable to pop,
  177. // just return it as a single batch.
  178. if (popped.length === 0 && this.items.length > 0) {
  179. const item = this.items.shift();
  180. popped.push(item);
  181. poppedSizeBytes += item.size;
  182. this.sizeBytes -= item.size;
  183. }
  184. return [
  185. popped.map((it) => ({ action: it.action, item: it.payload })),
  186. () => popped.forEach((it) => it.itemPromiseResolve()),
  187. ];
  188. }
  189. }
  190. exports.AutoBatchQueue = AutoBatchQueue;
  191. // 20 MB
  192. exports.DEFAULT_BATCH_SIZE_LIMIT_BYTES = 20_971_520;
  193. const SERVER_INFO_REQUEST_TIMEOUT = 2500;
  194. class Client {
  195. constructor(config = {}) {
  196. Object.defineProperty(this, "apiKey", {
  197. enumerable: true,
  198. configurable: true,
  199. writable: true,
  200. value: void 0
  201. });
  202. Object.defineProperty(this, "apiUrl", {
  203. enumerable: true,
  204. configurable: true,
  205. writable: true,
  206. value: void 0
  207. });
  208. Object.defineProperty(this, "webUrl", {
  209. enumerable: true,
  210. configurable: true,
  211. writable: true,
  212. value: void 0
  213. });
  214. Object.defineProperty(this, "caller", {
  215. enumerable: true,
  216. configurable: true,
  217. writable: true,
  218. value: void 0
  219. });
  220. Object.defineProperty(this, "batchIngestCaller", {
  221. enumerable: true,
  222. configurable: true,
  223. writable: true,
  224. value: void 0
  225. });
  226. Object.defineProperty(this, "timeout_ms", {
  227. enumerable: true,
  228. configurable: true,
  229. writable: true,
  230. value: void 0
  231. });
  232. Object.defineProperty(this, "_tenantId", {
  233. enumerable: true,
  234. configurable: true,
  235. writable: true,
  236. value: null
  237. });
  238. Object.defineProperty(this, "hideInputs", {
  239. enumerable: true,
  240. configurable: true,
  241. writable: true,
  242. value: void 0
  243. });
  244. Object.defineProperty(this, "hideOutputs", {
  245. enumerable: true,
  246. configurable: true,
  247. writable: true,
  248. value: void 0
  249. });
  250. Object.defineProperty(this, "tracingSampleRate", {
  251. enumerable: true,
  252. configurable: true,
  253. writable: true,
  254. value: void 0
  255. });
  256. Object.defineProperty(this, "filteredPostUuids", {
  257. enumerable: true,
  258. configurable: true,
  259. writable: true,
  260. value: new Set()
  261. });
  262. Object.defineProperty(this, "autoBatchTracing", {
  263. enumerable: true,
  264. configurable: true,
  265. writable: true,
  266. value: true
  267. });
  268. Object.defineProperty(this, "autoBatchQueue", {
  269. enumerable: true,
  270. configurable: true,
  271. writable: true,
  272. value: new AutoBatchQueue()
  273. });
  274. Object.defineProperty(this, "autoBatchTimeout", {
  275. enumerable: true,
  276. configurable: true,
  277. writable: true,
  278. value: void 0
  279. });
  280. Object.defineProperty(this, "autoBatchAggregationDelayMs", {
  281. enumerable: true,
  282. configurable: true,
  283. writable: true,
  284. value: 250
  285. });
  286. Object.defineProperty(this, "batchSizeBytesLimit", {
  287. enumerable: true,
  288. configurable: true,
  289. writable: true,
  290. value: void 0
  291. });
  292. Object.defineProperty(this, "fetchOptions", {
  293. enumerable: true,
  294. configurable: true,
  295. writable: true,
  296. value: void 0
  297. });
  298. Object.defineProperty(this, "settings", {
  299. enumerable: true,
  300. configurable: true,
  301. writable: true,
  302. value: void 0
  303. });
  304. Object.defineProperty(this, "blockOnRootRunFinalization", {
  305. enumerable: true,
  306. configurable: true,
  307. writable: true,
  308. value: (0, env_js_1.getEnvironmentVariable)("LANGSMITH_TRACING_BACKGROUND") === "false"
  309. });
  310. Object.defineProperty(this, "traceBatchConcurrency", {
  311. enumerable: true,
  312. configurable: true,
  313. writable: true,
  314. value: 5
  315. });
  316. Object.defineProperty(this, "_serverInfo", {
  317. enumerable: true,
  318. configurable: true,
  319. writable: true,
  320. value: void 0
  321. });
  322. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  323. Object.defineProperty(this, "_getServerInfoPromise", {
  324. enumerable: true,
  325. configurable: true,
  326. writable: true,
  327. value: void 0
  328. });
  329. Object.defineProperty(this, "manualFlushMode", {
  330. enumerable: true,
  331. configurable: true,
  332. writable: true,
  333. value: false
  334. });
  335. Object.defineProperty(this, "debug", {
  336. enumerable: true,
  337. configurable: true,
  338. writable: true,
  339. value: (0, env_js_1.getEnvironmentVariable)("LANGSMITH_DEBUG") === "true"
  340. });
  341. const defaultConfig = Client.getDefaultClientConfig();
  342. this.tracingSampleRate = getTracingSamplingRate(config.tracingSamplingRate);
  343. this.apiUrl = trimQuotes(config.apiUrl ?? defaultConfig.apiUrl) ?? "";
  344. if (this.apiUrl.endsWith("/")) {
  345. this.apiUrl = this.apiUrl.slice(0, -1);
  346. }
  347. this.apiKey = trimQuotes(config.apiKey ?? defaultConfig.apiKey);
  348. this.webUrl = trimQuotes(config.webUrl ?? defaultConfig.webUrl);
  349. if (this.webUrl?.endsWith("/")) {
  350. this.webUrl = this.webUrl.slice(0, -1);
  351. }
  352. this.timeout_ms = config.timeout_ms ?? 90_000;
  353. this.caller = new async_caller_js_1.AsyncCaller({
  354. ...(config.callerOptions ?? {}),
  355. debug: config.debug ?? this.debug,
  356. });
  357. this.traceBatchConcurrency =
  358. config.traceBatchConcurrency ?? this.traceBatchConcurrency;
  359. if (this.traceBatchConcurrency < 1) {
  360. throw new Error("Trace batch concurrency must be positive.");
  361. }
  362. this.debug = config.debug ?? this.debug;
  363. this.batchIngestCaller = new async_caller_js_1.AsyncCaller({
  364. maxRetries: 2,
  365. maxConcurrency: this.traceBatchConcurrency,
  366. ...(config.callerOptions ?? {}),
  367. onFailedResponseHook: handle429,
  368. debug: config.debug ?? this.debug,
  369. });
  370. this.hideInputs =
  371. config.hideInputs ?? config.anonymizer ?? defaultConfig.hideInputs;
  372. this.hideOutputs =
  373. config.hideOutputs ?? config.anonymizer ?? defaultConfig.hideOutputs;
  374. this.autoBatchTracing = config.autoBatchTracing ?? this.autoBatchTracing;
  375. this.blockOnRootRunFinalization =
  376. config.blockOnRootRunFinalization ?? this.blockOnRootRunFinalization;
  377. this.batchSizeBytesLimit = config.batchSizeBytesLimit;
  378. this.fetchOptions = config.fetchOptions || {};
  379. this.manualFlushMode = config.manualFlushMode ?? this.manualFlushMode;
  380. }
  381. static getDefaultClientConfig() {
  382. const apiKey = (0, env_js_1.getLangSmithEnvironmentVariable)("API_KEY");
  383. const apiUrl = (0, env_js_1.getLangSmithEnvironmentVariable)("ENDPOINT") ??
  384. "https://api.smith.langchain.com";
  385. const hideInputs = (0, env_js_1.getLangSmithEnvironmentVariable)("HIDE_INPUTS") === "true";
  386. const hideOutputs = (0, env_js_1.getLangSmithEnvironmentVariable)("HIDE_OUTPUTS") === "true";
  387. return {
  388. apiUrl: apiUrl,
  389. apiKey: apiKey,
  390. webUrl: undefined,
  391. hideInputs: hideInputs,
  392. hideOutputs: hideOutputs,
  393. };
  394. }
  395. getHostUrl() {
  396. if (this.webUrl) {
  397. return this.webUrl;
  398. }
  399. else if (isLocalhost(this.apiUrl)) {
  400. this.webUrl = "http://localhost:3000";
  401. return this.webUrl;
  402. }
  403. else if (this.apiUrl.endsWith("/api/v1")) {
  404. this.webUrl = this.apiUrl.replace("/api/v1", "");
  405. return this.webUrl;
  406. }
  407. else if (this.apiUrl.includes("/api") &&
  408. !this.apiUrl.split(".", 1)[0].endsWith("api")) {
  409. this.webUrl = this.apiUrl.replace("/api", "");
  410. return this.webUrl;
  411. }
  412. else if (this.apiUrl.split(".", 1)[0].includes("dev")) {
  413. this.webUrl = "https://dev.smith.langchain.com";
  414. return this.webUrl;
  415. }
  416. else if (this.apiUrl.split(".", 1)[0].includes("eu")) {
  417. this.webUrl = "https://eu.smith.langchain.com";
  418. return this.webUrl;
  419. }
  420. else if (this.apiUrl.split(".", 1)[0].includes("beta")) {
  421. this.webUrl = "https://beta.smith.langchain.com";
  422. return this.webUrl;
  423. }
  424. else {
  425. this.webUrl = "https://smith.langchain.com";
  426. return this.webUrl;
  427. }
  428. }
  429. get headers() {
  430. const headers = {
  431. "User-Agent": `langsmith-js/${index_js_1.__version__}`,
  432. };
  433. if (this.apiKey) {
  434. headers["x-api-key"] = `${this.apiKey}`;
  435. }
  436. return headers;
  437. }
  438. async processInputs(inputs) {
  439. if (this.hideInputs === false) {
  440. return inputs;
  441. }
  442. if (this.hideInputs === true) {
  443. return {};
  444. }
  445. if (typeof this.hideInputs === "function") {
  446. return this.hideInputs(inputs);
  447. }
  448. return inputs;
  449. }
  450. async processOutputs(outputs) {
  451. if (this.hideOutputs === false) {
  452. return outputs;
  453. }
  454. if (this.hideOutputs === true) {
  455. return {};
  456. }
  457. if (typeof this.hideOutputs === "function") {
  458. return this.hideOutputs(outputs);
  459. }
  460. return outputs;
  461. }
  462. async prepareRunCreateOrUpdateInputs(run) {
  463. const runParams = { ...run };
  464. if (runParams.inputs !== undefined) {
  465. runParams.inputs = await this.processInputs(runParams.inputs);
  466. }
  467. if (runParams.outputs !== undefined) {
  468. runParams.outputs = await this.processOutputs(runParams.outputs);
  469. }
  470. return runParams;
  471. }
  472. async _getResponse(path, queryParams) {
  473. const paramsString = queryParams?.toString() ?? "";
  474. const url = `${this.apiUrl}${path}?${paramsString}`;
  475. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), url, {
  476. method: "GET",
  477. headers: this.headers,
  478. signal: AbortSignal.timeout(this.timeout_ms),
  479. ...this.fetchOptions,
  480. });
  481. await (0, error_js_1.raiseForStatus)(response, `Failed to fetch ${path}`);
  482. return response;
  483. }
  484. async _get(path, queryParams) {
  485. const response = await this._getResponse(path, queryParams);
  486. return response.json();
  487. }
  488. async *_getPaginated(path, queryParams = new URLSearchParams(), transform) {
  489. let offset = Number(queryParams.get("offset")) || 0;
  490. const limit = Number(queryParams.get("limit")) || 100;
  491. while (true) {
  492. queryParams.set("offset", String(offset));
  493. queryParams.set("limit", String(limit));
  494. const url = `${this.apiUrl}${path}?${queryParams}`;
  495. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), url, {
  496. method: "GET",
  497. headers: this.headers,
  498. signal: AbortSignal.timeout(this.timeout_ms),
  499. ...this.fetchOptions,
  500. });
  501. await (0, error_js_1.raiseForStatus)(response, `Failed to fetch ${path}`);
  502. const items = transform
  503. ? transform(await response.json())
  504. : await response.json();
  505. if (items.length === 0) {
  506. break;
  507. }
  508. yield items;
  509. if (items.length < limit) {
  510. break;
  511. }
  512. offset += items.length;
  513. }
  514. }
  515. async *_getCursorPaginatedList(path, body = null, requestMethod = "POST", dataKey = "runs") {
  516. const bodyParams = body ? { ...body } : {};
  517. while (true) {
  518. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}${path}`, {
  519. method: requestMethod,
  520. headers: { ...this.headers, "Content-Type": "application/json" },
  521. signal: AbortSignal.timeout(this.timeout_ms),
  522. ...this.fetchOptions,
  523. body: JSON.stringify(bodyParams),
  524. });
  525. const responseBody = await response.json();
  526. if (!responseBody) {
  527. break;
  528. }
  529. if (!responseBody[dataKey]) {
  530. break;
  531. }
  532. yield responseBody[dataKey];
  533. const cursors = responseBody.cursors;
  534. if (!cursors) {
  535. break;
  536. }
  537. if (!cursors.next) {
  538. break;
  539. }
  540. bodyParams.cursor = cursors.next;
  541. }
  542. }
  543. // Allows mocking for tests
  544. _shouldSample() {
  545. if (this.tracingSampleRate === undefined) {
  546. return true;
  547. }
  548. return Math.random() < this.tracingSampleRate;
  549. }
  550. _filterForSampling(runs, patch = false) {
  551. if (this.tracingSampleRate === undefined) {
  552. return runs;
  553. }
  554. if (patch) {
  555. const sampled = [];
  556. for (const run of runs) {
  557. if (!this.filteredPostUuids.has(run.id)) {
  558. sampled.push(run);
  559. }
  560. else {
  561. this.filteredPostUuids.delete(run.id);
  562. }
  563. }
  564. return sampled;
  565. }
  566. else {
  567. // For new runs, sample at trace level to maintain consistency
  568. const sampled = [];
  569. for (const run of runs) {
  570. const traceId = run.trace_id ?? run.id;
  571. // If we've already made a decision about this trace, follow it
  572. if (this.filteredPostUuids.has(traceId)) {
  573. continue;
  574. }
  575. // For new traces, apply sampling
  576. if (run.id === traceId) {
  577. if (this._shouldSample()) {
  578. sampled.push(run);
  579. }
  580. else {
  581. this.filteredPostUuids.add(traceId);
  582. }
  583. }
  584. else {
  585. // Child runs follow their trace's sampling decision
  586. sampled.push(run);
  587. }
  588. }
  589. return sampled;
  590. }
  591. }
  592. async _getBatchSizeLimitBytes() {
  593. const serverInfo = await this._ensureServerInfo();
  594. return (this.batchSizeBytesLimit ??
  595. serverInfo.batch_ingest_config?.size_limit_bytes ??
  596. exports.DEFAULT_BATCH_SIZE_LIMIT_BYTES);
  597. }
  598. async _getMultiPartSupport() {
  599. const serverInfo = await this._ensureServerInfo();
  600. return (serverInfo.instance_flags?.dataset_examples_multipart_enabled ?? false);
  601. }
  602. drainAutoBatchQueue(batchSizeLimit) {
  603. const promises = [];
  604. while (this.autoBatchQueue.items.length > 0) {
  605. const [batch, done] = this.autoBatchQueue.pop(batchSizeLimit);
  606. if (!batch.length) {
  607. done();
  608. break;
  609. }
  610. const batchPromise = this._processBatch(batch, done).catch(console.error);
  611. promises.push(batchPromise);
  612. }
  613. return Promise.all(promises);
  614. }
  615. async _processBatch(batch, done) {
  616. if (!batch.length) {
  617. done();
  618. return;
  619. }
  620. try {
  621. const ingestParams = {
  622. runCreates: batch
  623. .filter((item) => item.action === "create")
  624. .map((item) => item.item),
  625. runUpdates: batch
  626. .filter((item) => item.action === "update")
  627. .map((item) => item.item),
  628. };
  629. const serverInfo = await this._ensureServerInfo();
  630. if (serverInfo?.batch_ingest_config?.use_multipart_endpoint) {
  631. await this.multipartIngestRuns(ingestParams);
  632. }
  633. else {
  634. await this.batchIngestRuns(ingestParams);
  635. }
  636. }
  637. finally {
  638. done();
  639. }
  640. }
  641. async processRunOperation(item) {
  642. clearTimeout(this.autoBatchTimeout);
  643. this.autoBatchTimeout = undefined;
  644. if (item.action === "create") {
  645. item.item = mergeRuntimeEnvIntoRunCreate(item.item);
  646. }
  647. const itemPromise = this.autoBatchQueue.push(item);
  648. if (this.manualFlushMode) {
  649. // Rely on manual flushing in serverless environments
  650. return itemPromise;
  651. }
  652. const sizeLimitBytes = await this._getBatchSizeLimitBytes();
  653. if (this.autoBatchQueue.sizeBytes > sizeLimitBytes) {
  654. void this.drainAutoBatchQueue(sizeLimitBytes);
  655. }
  656. if (this.autoBatchQueue.items.length > 0) {
  657. this.autoBatchTimeout = setTimeout(() => {
  658. this.autoBatchTimeout = undefined;
  659. void this.drainAutoBatchQueue(sizeLimitBytes);
  660. }, this.autoBatchAggregationDelayMs);
  661. }
  662. return itemPromise;
  663. }
  664. async _getServerInfo() {
  665. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/info`, {
  666. method: "GET",
  667. headers: { Accept: "application/json" },
  668. signal: AbortSignal.timeout(SERVER_INFO_REQUEST_TIMEOUT),
  669. ...this.fetchOptions,
  670. });
  671. await (0, error_js_1.raiseForStatus)(response, "get server info");
  672. const json = await response.json();
  673. if (this.debug) {
  674. console.log("\n=== LangSmith Server Configuration ===\n" +
  675. JSON.stringify(json, null, 2) +
  676. "\n");
  677. }
  678. return json;
  679. }
  680. async _ensureServerInfo() {
  681. if (this._getServerInfoPromise === undefined) {
  682. this._getServerInfoPromise = (async () => {
  683. if (this._serverInfo === undefined) {
  684. try {
  685. this._serverInfo = await this._getServerInfo();
  686. }
  687. catch (e) {
  688. console.warn(`[WARNING]: LangSmith failed to fetch info on supported operations with status code ${e.status}. Falling back to batch operations and default limits.`);
  689. }
  690. }
  691. return this._serverInfo ?? {};
  692. })();
  693. }
  694. return this._getServerInfoPromise.then((serverInfo) => {
  695. if (this._serverInfo === undefined) {
  696. this._getServerInfoPromise = undefined;
  697. }
  698. return serverInfo;
  699. });
  700. }
  701. async _getSettings() {
  702. if (!this.settings) {
  703. this.settings = this._get("/settings");
  704. }
  705. return await this.settings;
  706. }
  707. /**
  708. * Flushes current queued traces.
  709. */
  710. async flush() {
  711. const sizeLimitBytes = await this._getBatchSizeLimitBytes();
  712. await this.drainAutoBatchQueue(sizeLimitBytes);
  713. }
  714. async createRun(run) {
  715. if (!this._filterForSampling([run]).length) {
  716. return;
  717. }
  718. const headers = { ...this.headers, "Content-Type": "application/json" };
  719. const session_name = run.project_name;
  720. delete run.project_name;
  721. const runCreate = await this.prepareRunCreateOrUpdateInputs({
  722. session_name,
  723. ...run,
  724. start_time: run.start_time ?? Date.now(),
  725. });
  726. if (this.autoBatchTracing &&
  727. runCreate.trace_id !== undefined &&
  728. runCreate.dotted_order !== undefined) {
  729. void this.processRunOperation({
  730. action: "create",
  731. item: runCreate,
  732. }).catch(console.error);
  733. return;
  734. }
  735. const mergedRunCreateParam = mergeRuntimeEnvIntoRunCreate(runCreate);
  736. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/runs`, {
  737. method: "POST",
  738. headers,
  739. body: (0, index_js_2.serialize)(mergedRunCreateParam, `Creating run with id: ${mergedRunCreateParam.id}`),
  740. signal: AbortSignal.timeout(this.timeout_ms),
  741. ...this.fetchOptions,
  742. });
  743. await (0, error_js_1.raiseForStatus)(response, "create run", true);
  744. }
  745. /**
  746. * Batch ingest/upsert multiple runs in the Langsmith system.
  747. * @param runs
  748. */
  749. async batchIngestRuns({ runCreates, runUpdates, }) {
  750. if (runCreates === undefined && runUpdates === undefined) {
  751. return;
  752. }
  753. let preparedCreateParams = await Promise.all(runCreates?.map((create) => this.prepareRunCreateOrUpdateInputs(create)) ?? []);
  754. let preparedUpdateParams = await Promise.all(runUpdates?.map((update) => this.prepareRunCreateOrUpdateInputs(update)) ?? []);
  755. if (preparedCreateParams.length > 0 && preparedUpdateParams.length > 0) {
  756. const createById = preparedCreateParams.reduce((params, run) => {
  757. if (!run.id) {
  758. return params;
  759. }
  760. params[run.id] = run;
  761. return params;
  762. }, {});
  763. const standaloneUpdates = [];
  764. for (const updateParam of preparedUpdateParams) {
  765. if (updateParam.id !== undefined && createById[updateParam.id]) {
  766. createById[updateParam.id] = {
  767. ...createById[updateParam.id],
  768. ...updateParam,
  769. };
  770. }
  771. else {
  772. standaloneUpdates.push(updateParam);
  773. }
  774. }
  775. preparedCreateParams = Object.values(createById);
  776. preparedUpdateParams = standaloneUpdates;
  777. }
  778. const rawBatch = {
  779. post: preparedCreateParams,
  780. patch: preparedUpdateParams,
  781. };
  782. if (!rawBatch.post.length && !rawBatch.patch.length) {
  783. return;
  784. }
  785. const batchChunks = {
  786. post: [],
  787. patch: [],
  788. };
  789. for (const k of ["post", "patch"]) {
  790. const key = k;
  791. const batchItems = rawBatch[key].reverse();
  792. let batchItem = batchItems.pop();
  793. while (batchItem !== undefined) {
  794. // Type is wrong but this is a deprecated code path anyway
  795. batchChunks[key].push(batchItem);
  796. batchItem = batchItems.pop();
  797. }
  798. }
  799. if (batchChunks.post.length > 0 || batchChunks.patch.length > 0) {
  800. const runIds = batchChunks.post
  801. .map((item) => item.id)
  802. .concat(batchChunks.patch.map((item) => item.id))
  803. .join(",");
  804. await this._postBatchIngestRuns((0, index_js_2.serialize)(batchChunks, `Ingesting runs with ids: ${runIds}`));
  805. }
  806. }
  807. async _postBatchIngestRuns(body) {
  808. const headers = {
  809. ...this.headers,
  810. "Content-Type": "application/json",
  811. Accept: "application/json",
  812. };
  813. const response = await this.batchIngestCaller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/runs/batch`, {
  814. method: "POST",
  815. headers,
  816. body: body,
  817. signal: AbortSignal.timeout(this.timeout_ms),
  818. ...this.fetchOptions,
  819. });
  820. await (0, error_js_1.raiseForStatus)(response, "batch create run", true);
  821. }
  822. /**
  823. * Batch ingest/upsert multiple runs in the Langsmith system.
  824. * @param runs
  825. */
  826. async multipartIngestRuns({ runCreates, runUpdates, }) {
  827. if (runCreates === undefined && runUpdates === undefined) {
  828. return;
  829. }
  830. // transform and convert to dicts
  831. const allAttachments = {};
  832. let preparedCreateParams = [];
  833. for (const create of runCreates ?? []) {
  834. const preparedCreate = await this.prepareRunCreateOrUpdateInputs(create);
  835. if (preparedCreate.id !== undefined &&
  836. preparedCreate.attachments !== undefined) {
  837. allAttachments[preparedCreate.id] = preparedCreate.attachments;
  838. }
  839. delete preparedCreate.attachments;
  840. preparedCreateParams.push(preparedCreate);
  841. }
  842. let preparedUpdateParams = [];
  843. for (const update of runUpdates ?? []) {
  844. preparedUpdateParams.push(await this.prepareRunCreateOrUpdateInputs(update));
  845. }
  846. // require trace_id and dotted_order
  847. const invalidRunCreate = preparedCreateParams.find((runCreate) => {
  848. return (runCreate.trace_id === undefined || runCreate.dotted_order === undefined);
  849. });
  850. if (invalidRunCreate !== undefined) {
  851. throw new Error(`Multipart ingest requires "trace_id" and "dotted_order" to be set when creating a run`);
  852. }
  853. const invalidRunUpdate = preparedUpdateParams.find((runUpdate) => {
  854. return (runUpdate.trace_id === undefined || runUpdate.dotted_order === undefined);
  855. });
  856. if (invalidRunUpdate !== undefined) {
  857. throw new Error(`Multipart ingest requires "trace_id" and "dotted_order" to be set when updating a run`);
  858. }
  859. // combine post and patch dicts where possible
  860. if (preparedCreateParams.length > 0 && preparedUpdateParams.length > 0) {
  861. const createById = preparedCreateParams.reduce((params, run) => {
  862. if (!run.id) {
  863. return params;
  864. }
  865. params[run.id] = run;
  866. return params;
  867. }, {});
  868. const standaloneUpdates = [];
  869. for (const updateParam of preparedUpdateParams) {
  870. if (updateParam.id !== undefined && createById[updateParam.id]) {
  871. createById[updateParam.id] = {
  872. ...createById[updateParam.id],
  873. ...updateParam,
  874. };
  875. }
  876. else {
  877. standaloneUpdates.push(updateParam);
  878. }
  879. }
  880. preparedCreateParams = Object.values(createById);
  881. preparedUpdateParams = standaloneUpdates;
  882. }
  883. if (preparedCreateParams.length === 0 &&
  884. preparedUpdateParams.length === 0) {
  885. return;
  886. }
  887. // send the runs in multipart requests
  888. const accumulatedContext = [];
  889. const accumulatedParts = [];
  890. for (const [method, payloads] of [
  891. ["post", preparedCreateParams],
  892. ["patch", preparedUpdateParams],
  893. ]) {
  894. for (const originalPayload of payloads) {
  895. // collect fields to be sent as separate parts
  896. const { inputs, outputs, events, attachments, ...payload } = originalPayload;
  897. const fields = { inputs, outputs, events };
  898. // encode the main run payload
  899. const stringifiedPayload = (0, index_js_2.serialize)(payload, `Serializing for multipart ingestion of run with id: ${payload.id}`);
  900. accumulatedParts.push({
  901. name: `${method}.${payload.id}`,
  902. payload: new Blob([stringifiedPayload], {
  903. type: `application/json; length=${stringifiedPayload.length}`, // encoding=gzip
  904. }),
  905. });
  906. // encode the fields we collected
  907. for (const [key, value] of Object.entries(fields)) {
  908. if (value === undefined) {
  909. continue;
  910. }
  911. const stringifiedValue = (0, index_js_2.serialize)(value, `Serializing ${key} for multipart ingestion of run with id: ${payload.id}`);
  912. accumulatedParts.push({
  913. name: `${method}.${payload.id}.${key}`,
  914. payload: new Blob([stringifiedValue], {
  915. type: `application/json; length=${stringifiedValue.length}`,
  916. }),
  917. });
  918. }
  919. // encode the attachments
  920. if (payload.id !== undefined) {
  921. const attachments = allAttachments[payload.id];
  922. if (attachments) {
  923. delete allAttachments[payload.id];
  924. for (const [name, attachment] of Object.entries(attachments)) {
  925. let contentType;
  926. let content;
  927. if (Array.isArray(attachment)) {
  928. [contentType, content] = attachment;
  929. }
  930. else {
  931. contentType = attachment.mimeType;
  932. content = attachment.data;
  933. }
  934. // Validate that the attachment name doesn't contain a '.'
  935. if (name.includes(".")) {
  936. console.warn(`Skipping attachment '${name}' for run ${payload.id}: Invalid attachment name. ` +
  937. `Attachment names must not contain periods ('.'). Please rename the attachment and try again.`);
  938. continue;
  939. }
  940. accumulatedParts.push({
  941. name: `attachment.${payload.id}.${name}`,
  942. payload: new Blob([content], {
  943. type: `${contentType}; length=${content.byteLength}`,
  944. }),
  945. });
  946. }
  947. }
  948. }
  949. // compute context
  950. accumulatedContext.push(`trace=${payload.trace_id},id=${payload.id}`);
  951. }
  952. }
  953. await this._sendMultipartRequest(accumulatedParts, accumulatedContext.join("; "));
  954. }
  955. async _createNodeFetchBody(parts, boundary) {
  956. // Create multipart form data manually using Blobs
  957. const chunks = [];
  958. for (const part of parts) {
  959. // Add field boundary
  960. chunks.push(new Blob([`--${boundary}\r\n`]));
  961. chunks.push(new Blob([
  962. `Content-Disposition: form-data; name="${part.name}"\r\n`,
  963. `Content-Type: ${part.payload.type}\r\n\r\n`,
  964. ]));
  965. chunks.push(part.payload);
  966. chunks.push(new Blob(["\r\n"]));
  967. }
  968. // Add final boundary
  969. chunks.push(new Blob([`--${boundary}--\r\n`]));
  970. // Combine all chunks into a single Blob
  971. const body = new Blob(chunks);
  972. // Convert Blob to ArrayBuffer for compatibility
  973. const arrayBuffer = await body.arrayBuffer();
  974. return arrayBuffer;
  975. }
  976. async _createMultipartStream(parts, boundary) {
  977. const encoder = new TextEncoder();
  978. // Create a ReadableStream for streaming the multipart data
  979. // Only do special handling if we're using node-fetch
  980. const stream = new ReadableStream({
  981. async start(controller) {
  982. // Helper function to write a chunk to the stream
  983. const writeChunk = async (chunk) => {
  984. if (typeof chunk === "string") {
  985. controller.enqueue(encoder.encode(chunk));
  986. }
  987. else {
  988. controller.enqueue(chunk);
  989. }
  990. };
  991. // Write each part to the stream
  992. for (const part of parts) {
  993. // Write boundary and headers
  994. await writeChunk(`--${boundary}\r\n`);
  995. await writeChunk(`Content-Disposition: form-data; name="${part.name}"\r\n`);
  996. await writeChunk(`Content-Type: ${part.payload.type}\r\n\r\n`);
  997. // Write the payload
  998. const payloadStream = part.payload.stream();
  999. const reader = payloadStream.getReader();
  1000. try {
  1001. let result;
  1002. while (!(result = await reader.read()).done) {
  1003. controller.enqueue(result.value);
  1004. }
  1005. }
  1006. finally {
  1007. reader.releaseLock();
  1008. }
  1009. await writeChunk("\r\n");
  1010. }
  1011. // Write final boundary
  1012. await writeChunk(`--${boundary}--\r\n`);
  1013. controller.close();
  1014. },
  1015. });
  1016. return stream;
  1017. }
  1018. async _sendMultipartRequest(parts, context) {
  1019. try {
  1020. // Create multipart form data boundary
  1021. const boundary = "----LangSmithFormBoundary" + Math.random().toString(36).slice(2);
  1022. const body = await ((0, fetch_js_1._globalFetchImplementationIsNodeFetch)()
  1023. ? this._createNodeFetchBody(parts, boundary)
  1024. : this._createMultipartStream(parts, boundary));
  1025. const res = await this.batchIngestCaller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/runs/multipart`, {
  1026. method: "POST",
  1027. headers: {
  1028. ...this.headers,
  1029. "Content-Type": `multipart/form-data; boundary=${boundary}`,
  1030. },
  1031. body,
  1032. duplex: "half",
  1033. signal: AbortSignal.timeout(this.timeout_ms),
  1034. ...this.fetchOptions,
  1035. });
  1036. await (0, error_js_1.raiseForStatus)(res, "ingest multipart runs", true);
  1037. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  1038. }
  1039. catch (e) {
  1040. console.warn(`${e.message.trim()}\n\nContext: ${context}`);
  1041. }
  1042. }
  1043. async updateRun(runId, run) {
  1044. (0, _uuid_js_1.assertUuid)(runId);
  1045. if (run.inputs) {
  1046. run.inputs = await this.processInputs(run.inputs);
  1047. }
  1048. if (run.outputs) {
  1049. run.outputs = await this.processOutputs(run.outputs);
  1050. }
  1051. // TODO: Untangle types
  1052. const data = { ...run, id: runId };
  1053. if (!this._filterForSampling([data], true).length) {
  1054. return;
  1055. }
  1056. if (this.autoBatchTracing &&
  1057. data.trace_id !== undefined &&
  1058. data.dotted_order !== undefined) {
  1059. if (run.end_time !== undefined &&
  1060. data.parent_run_id === undefined &&
  1061. this.blockOnRootRunFinalization &&
  1062. !this.manualFlushMode) {
  1063. // Trigger batches as soon as a root trace ends and wait to ensure trace finishes
  1064. // in serverless environments.
  1065. await this.processRunOperation({ action: "update", item: data }).catch(console.error);
  1066. return;
  1067. }
  1068. else {
  1069. void this.processRunOperation({ action: "update", item: data }).catch(console.error);
  1070. }
  1071. return;
  1072. }
  1073. const headers = { ...this.headers, "Content-Type": "application/json" };
  1074. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/runs/${runId}`, {
  1075. method: "PATCH",
  1076. headers,
  1077. body: (0, index_js_2.serialize)(run, `Serializing payload to update run with id: ${runId}`),
  1078. signal: AbortSignal.timeout(this.timeout_ms),
  1079. ...this.fetchOptions,
  1080. });
  1081. await (0, error_js_1.raiseForStatus)(response, "update run", true);
  1082. }
  1083. async readRun(runId, { loadChildRuns } = { loadChildRuns: false }) {
  1084. (0, _uuid_js_1.assertUuid)(runId);
  1085. let run = await this._get(`/runs/${runId}`);
  1086. if (loadChildRuns && run.child_run_ids) {
  1087. run = await this._loadChildRuns(run);
  1088. }
  1089. return run;
  1090. }
  1091. async getRunUrl({ runId, run, projectOpts, }) {
  1092. if (run !== undefined) {
  1093. let sessionId;
  1094. if (run.session_id) {
  1095. sessionId = run.session_id;
  1096. }
  1097. else if (projectOpts?.projectName) {
  1098. sessionId = (await this.readProject({ projectName: projectOpts?.projectName })).id;
  1099. }
  1100. else if (projectOpts?.projectId) {
  1101. sessionId = projectOpts?.projectId;
  1102. }
  1103. else {
  1104. const project = await this.readProject({
  1105. projectName: (0, env_js_1.getLangSmithEnvironmentVariable)("PROJECT") || "default",
  1106. });
  1107. sessionId = project.id;
  1108. }
  1109. const tenantId = await this._getTenantId();
  1110. return `${this.getHostUrl()}/o/${tenantId}/projects/p/${sessionId}/r/${run.id}?poll=true`;
  1111. }
  1112. else if (runId !== undefined) {
  1113. const run_ = await this.readRun(runId);
  1114. if (!run_.app_path) {
  1115. throw new Error(`Run ${runId} has no app_path`);
  1116. }
  1117. const baseUrl = this.getHostUrl();
  1118. return `${baseUrl}${run_.app_path}`;
  1119. }
  1120. else {
  1121. throw new Error("Must provide either runId or run");
  1122. }
  1123. }
  1124. async _loadChildRuns(run) {
  1125. const childRuns = await toArray(this.listRuns({ id: run.child_run_ids }));
  1126. const treemap = {};
  1127. const runs = {};
  1128. // TODO: make dotted order required when the migration finishes
  1129. childRuns.sort((a, b) => (a?.dotted_order ?? "").localeCompare(b?.dotted_order ?? ""));
  1130. for (const childRun of childRuns) {
  1131. if (childRun.parent_run_id === null ||
  1132. childRun.parent_run_id === undefined) {
  1133. throw new Error(`Child run ${childRun.id} has no parent`);
  1134. }
  1135. if (!(childRun.parent_run_id in treemap)) {
  1136. treemap[childRun.parent_run_id] = [];
  1137. }
  1138. treemap[childRun.parent_run_id].push(childRun);
  1139. runs[childRun.id] = childRun;
  1140. }
  1141. run.child_runs = treemap[run.id] || [];
  1142. for (const runId in treemap) {
  1143. if (runId !== run.id) {
  1144. runs[runId].child_runs = treemap[runId];
  1145. }
  1146. }
  1147. return run;
  1148. }
  1149. /**
  1150. * List runs from the LangSmith server.
  1151. * @param projectId - The ID of the project to filter by.
  1152. * @param projectName - The name of the project to filter by.
  1153. * @param parentRunId - The ID of the parent run to filter by.
  1154. * @param traceId - The ID of the trace to filter by.
  1155. * @param referenceExampleId - The ID of the reference example to filter by.
  1156. * @param startTime - The start time to filter by.
  1157. * @param isRoot - Indicates whether to only return root runs.
  1158. * @param runType - The run type to filter by.
  1159. * @param error - Indicates whether to filter by error runs.
  1160. * @param id - The ID of the run to filter by.
  1161. * @param query - The query string to filter by.
  1162. * @param filter - The filter string to apply to the run spans.
  1163. * @param traceFilter - The filter string to apply on the root run of the trace.
  1164. * @param treeFilter - The filter string to apply on other runs in the trace.
  1165. * @param limit - The maximum number of runs to retrieve.
  1166. * @returns {AsyncIterable<Run>} - The runs.
  1167. *
  1168. * @example
  1169. * // List all runs in a project
  1170. * const projectRuns = client.listRuns({ projectName: "<your_project>" });
  1171. *
  1172. * @example
  1173. * // List LLM and Chat runs in the last 24 hours
  1174. * const todaysLLMRuns = client.listRuns({
  1175. * projectName: "<your_project>",
  1176. * start_time: new Date(Date.now() - 24 * 60 * 60 * 1000),
  1177. * run_type: "llm",
  1178. * });
  1179. *
  1180. * @example
  1181. * // List traces in a project
  1182. * const rootRuns = client.listRuns({
  1183. * projectName: "<your_project>",
  1184. * execution_order: 1,
  1185. * });
  1186. *
  1187. * @example
  1188. * // List runs without errors
  1189. * const correctRuns = client.listRuns({
  1190. * projectName: "<your_project>",
  1191. * error: false,
  1192. * });
  1193. *
  1194. * @example
  1195. * // List runs by run ID
  1196. * const runIds = [
  1197. * "a36092d2-4ad5-4fb4-9c0d-0dba9a2ed836",
  1198. * "9398e6be-964f-4aa4-8ae9-ad78cd4b7074",
  1199. * ];
  1200. * const selectedRuns = client.listRuns({ run_ids: runIds });
  1201. *
  1202. * @example
  1203. * // List all "chain" type runs that took more than 10 seconds and had `total_tokens` greater than 5000
  1204. * const chainRuns = client.listRuns({
  1205. * projectName: "<your_project>",
  1206. * filter: 'and(eq(run_type, "chain"), gt(latency, 10), gt(total_tokens, 5000))',
  1207. * });
  1208. *
  1209. * @example
  1210. * // List all runs called "extractor" whose root of the trace was assigned feedback "user_score" score of 1
  1211. * const goodExtractorRuns = client.listRuns({
  1212. * projectName: "<your_project>",
  1213. * filter: 'eq(name, "extractor")',
  1214. * traceFilter: 'and(eq(feedback_key, "user_score"), eq(feedback_score, 1))',
  1215. * });
  1216. *
  1217. * @example
  1218. * // List all runs that started after a specific timestamp and either have "error" not equal to null or a "Correctness" feedback score equal to 0
  1219. * const complexRuns = client.listRuns({
  1220. * projectName: "<your_project>",
  1221. * filter: 'and(gt(start_time, "2023-07-15T12:34:56Z"), or(neq(error, null), and(eq(feedback_key, "Correctness"), eq(feedback_score, 0.0))))',
  1222. * });
  1223. *
  1224. * @example
  1225. * // List all runs where `tags` include "experimental" or "beta" and `latency` is greater than 2 seconds
  1226. * const taggedRuns = client.listRuns({
  1227. * projectName: "<your_project>",
  1228. * filter: 'and(or(has(tags, "experimental"), has(tags, "beta")), gt(latency, 2))',
  1229. * });
  1230. */
  1231. async *listRuns(props) {
  1232. const { projectId, projectName, parentRunId, traceId, referenceExampleId, startTime, executionOrder, isRoot, runType, error, id, query, filter, traceFilter, treeFilter, limit, select, order, } = props;
  1233. let projectIds = [];
  1234. if (projectId) {
  1235. projectIds = Array.isArray(projectId) ? projectId : [projectId];
  1236. }
  1237. if (projectName) {
  1238. const projectNames = Array.isArray(projectName)
  1239. ? projectName
  1240. : [projectName];
  1241. const projectIds_ = await Promise.all(projectNames.map((name) => this.readProject({ projectName: name }).then((project) => project.id)));
  1242. projectIds.push(...projectIds_);
  1243. }
  1244. const default_select = [
  1245. "app_path",
  1246. "child_run_ids",
  1247. "completion_cost",
  1248. "completion_tokens",
  1249. "dotted_order",
  1250. "end_time",
  1251. "error",
  1252. "events",
  1253. "extra",
  1254. "feedback_stats",
  1255. "first_token_time",
  1256. "id",
  1257. "inputs",
  1258. "name",
  1259. "outputs",
  1260. "parent_run_id",
  1261. "parent_run_ids",
  1262. "prompt_cost",
  1263. "prompt_tokens",
  1264. "reference_example_id",
  1265. "run_type",
  1266. "session_id",
  1267. "start_time",
  1268. "status",
  1269. "tags",
  1270. "total_cost",
  1271. "total_tokens",
  1272. "trace_id",
  1273. ];
  1274. const body = {
  1275. session: projectIds.length ? projectIds : null,
  1276. run_type: runType,
  1277. reference_example: referenceExampleId,
  1278. query,
  1279. filter,
  1280. trace_filter: traceFilter,
  1281. tree_filter: treeFilter,
  1282. execution_order: executionOrder,
  1283. parent_run: parentRunId,
  1284. start_time: startTime ? startTime.toISOString() : null,
  1285. error,
  1286. id,
  1287. limit,
  1288. trace: traceId,
  1289. select: select ? select : default_select,
  1290. is_root: isRoot,
  1291. order,
  1292. };
  1293. let runsYielded = 0;
  1294. for await (const runs of this._getCursorPaginatedList("/runs/query", body)) {
  1295. if (limit) {
  1296. if (runsYielded >= limit) {
  1297. break;
  1298. }
  1299. if (runs.length + runsYielded > limit) {
  1300. const newRuns = runs.slice(0, limit - runsYielded);
  1301. yield* newRuns;
  1302. break;
  1303. }
  1304. runsYielded += runs.length;
  1305. yield* runs;
  1306. }
  1307. else {
  1308. yield* runs;
  1309. }
  1310. }
  1311. }
  1312. async *listGroupRuns(props) {
  1313. const { projectId, projectName, groupBy, filter, startTime, endTime, limit, offset, } = props;
  1314. const sessionId = projectId || (await this.readProject({ projectName })).id;
  1315. const baseBody = {
  1316. session_id: sessionId,
  1317. group_by: groupBy,
  1318. filter,
  1319. start_time: startTime ? startTime.toISOString() : null,
  1320. end_time: endTime ? endTime.toISOString() : null,
  1321. limit: Number(limit) || 100,
  1322. };
  1323. let currentOffset = Number(offset) || 0;
  1324. const path = "/runs/group";
  1325. const url = `${this.apiUrl}${path}`;
  1326. while (true) {
  1327. const currentBody = {
  1328. ...baseBody,
  1329. offset: currentOffset,
  1330. };
  1331. // Remove undefined values from the payload
  1332. const filteredPayload = Object.fromEntries(Object.entries(currentBody).filter(([_, value]) => value !== undefined));
  1333. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(), url, {
  1334. method: "POST",
  1335. headers: { ...this.headers, "Content-Type": "application/json" },
  1336. body: JSON.stringify(filteredPayload),
  1337. signal: AbortSignal.timeout(this.timeout_ms),
  1338. ...this.fetchOptions,
  1339. });
  1340. await (0, error_js_1.raiseForStatus)(response, `Failed to fetch ${path}`);
  1341. const items = await response.json();
  1342. const { groups, total } = items;
  1343. if (groups.length === 0) {
  1344. break;
  1345. }
  1346. for (const thread of groups) {
  1347. yield thread;
  1348. }
  1349. currentOffset += groups.length;
  1350. if (currentOffset >= total) {
  1351. break;
  1352. }
  1353. }
  1354. }
  1355. async getRunStats({ id, trace, parentRun, runType, projectNames, projectIds, referenceExampleIds, startTime, endTime, error, query, filter, traceFilter, treeFilter, isRoot, dataSourceType, }) {
  1356. let projectIds_ = projectIds || [];
  1357. if (projectNames) {
  1358. projectIds_ = [
  1359. ...(projectIds || []),
  1360. ...(await Promise.all(projectNames.map((name) => this.readProject({ projectName: name }).then((project) => project.id)))),
  1361. ];
  1362. }
  1363. const payload = {
  1364. id,
  1365. trace,
  1366. parent_run: parentRun,
  1367. run_type: runType,
  1368. session: projectIds_,
  1369. reference_example: referenceExampleIds,
  1370. start_time: startTime,
  1371. end_time: endTime,
  1372. error,
  1373. query,
  1374. filter,
  1375. trace_filter: traceFilter,
  1376. tree_filter: treeFilter,
  1377. is_root: isRoot,
  1378. data_source_type: dataSourceType,
  1379. };
  1380. // Remove undefined values from the payload
  1381. const filteredPayload = Object.fromEntries(Object.entries(payload).filter(([_, value]) => value !== undefined));
  1382. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/runs/stats`, {
  1383. method: "POST",
  1384. headers: this.headers,
  1385. body: JSON.stringify(filteredPayload),
  1386. signal: AbortSignal.timeout(this.timeout_ms),
  1387. ...this.fetchOptions,
  1388. });
  1389. const result = await response.json();
  1390. return result;
  1391. }
  1392. async shareRun(runId, { shareId } = {}) {
  1393. const data = {
  1394. run_id: runId,
  1395. share_token: shareId || uuid.v4(),
  1396. };
  1397. (0, _uuid_js_1.assertUuid)(runId);
  1398. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/runs/${runId}/share`, {
  1399. method: "PUT",
  1400. headers: this.headers,
  1401. body: JSON.stringify(data),
  1402. signal: AbortSignal.timeout(this.timeout_ms),
  1403. ...this.fetchOptions,
  1404. });
  1405. const result = await response.json();
  1406. if (result === null || !("share_token" in result)) {
  1407. throw new Error("Invalid response from server");
  1408. }
  1409. return `${this.getHostUrl()}/public/${result["share_token"]}/r`;
  1410. }
  1411. async unshareRun(runId) {
  1412. (0, _uuid_js_1.assertUuid)(runId);
  1413. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/runs/${runId}/share`, {
  1414. method: "DELETE",
  1415. headers: this.headers,
  1416. signal: AbortSignal.timeout(this.timeout_ms),
  1417. ...this.fetchOptions,
  1418. });
  1419. await (0, error_js_1.raiseForStatus)(response, "unshare run", true);
  1420. }
  1421. async readRunSharedLink(runId) {
  1422. (0, _uuid_js_1.assertUuid)(runId);
  1423. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/runs/${runId}/share`, {
  1424. method: "GET",
  1425. headers: this.headers,
  1426. signal: AbortSignal.timeout(this.timeout_ms),
  1427. ...this.fetchOptions,
  1428. });
  1429. const result = await response.json();
  1430. if (result === null || !("share_token" in result)) {
  1431. return undefined;
  1432. }
  1433. return `${this.getHostUrl()}/public/${result["share_token"]}/r`;
  1434. }
  1435. async listSharedRuns(shareToken, { runIds, } = {}) {
  1436. const queryParams = new URLSearchParams({
  1437. share_token: shareToken,
  1438. });
  1439. if (runIds !== undefined) {
  1440. for (const runId of runIds) {
  1441. queryParams.append("id", runId);
  1442. }
  1443. }
  1444. (0, _uuid_js_1.assertUuid)(shareToken);
  1445. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/public/${shareToken}/runs${queryParams}`, {
  1446. method: "GET",
  1447. headers: this.headers,
  1448. signal: AbortSignal.timeout(this.timeout_ms),
  1449. ...this.fetchOptions,
  1450. });
  1451. const runs = await response.json();
  1452. return runs;
  1453. }
  1454. async readDatasetSharedSchema(datasetId, datasetName) {
  1455. if (!datasetId && !datasetName) {
  1456. throw new Error("Either datasetId or datasetName must be given");
  1457. }
  1458. if (!datasetId) {
  1459. const dataset = await this.readDataset({ datasetName });
  1460. datasetId = dataset.id;
  1461. }
  1462. (0, _uuid_js_1.assertUuid)(datasetId);
  1463. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/datasets/${datasetId}/share`, {
  1464. method: "GET",
  1465. headers: this.headers,
  1466. signal: AbortSignal.timeout(this.timeout_ms),
  1467. ...this.fetchOptions,
  1468. });
  1469. const shareSchema = await response.json();
  1470. shareSchema.url = `${this.getHostUrl()}/public/${shareSchema.share_token}/d`;
  1471. return shareSchema;
  1472. }
  1473. async shareDataset(datasetId, datasetName) {
  1474. if (!datasetId && !datasetName) {
  1475. throw new Error("Either datasetId or datasetName must be given");
  1476. }
  1477. if (!datasetId) {
  1478. const dataset = await this.readDataset({ datasetName });
  1479. datasetId = dataset.id;
  1480. }
  1481. const data = {
  1482. dataset_id: datasetId,
  1483. };
  1484. (0, _uuid_js_1.assertUuid)(datasetId);
  1485. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/datasets/${datasetId}/share`, {
  1486. method: "PUT",
  1487. headers: this.headers,
  1488. body: JSON.stringify(data),
  1489. signal: AbortSignal.timeout(this.timeout_ms),
  1490. ...this.fetchOptions,
  1491. });
  1492. const shareSchema = await response.json();
  1493. shareSchema.url = `${this.getHostUrl()}/public/${shareSchema.share_token}/d`;
  1494. return shareSchema;
  1495. }
  1496. async unshareDataset(datasetId) {
  1497. (0, _uuid_js_1.assertUuid)(datasetId);
  1498. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/datasets/${datasetId}/share`, {
  1499. method: "DELETE",
  1500. headers: this.headers,
  1501. signal: AbortSignal.timeout(this.timeout_ms),
  1502. ...this.fetchOptions,
  1503. });
  1504. await (0, error_js_1.raiseForStatus)(response, "unshare dataset", true);
  1505. }
  1506. async readSharedDataset(shareToken) {
  1507. (0, _uuid_js_1.assertUuid)(shareToken);
  1508. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/public/${shareToken}/datasets`, {
  1509. method: "GET",
  1510. headers: this.headers,
  1511. signal: AbortSignal.timeout(this.timeout_ms),
  1512. ...this.fetchOptions,
  1513. });
  1514. const dataset = await response.json();
  1515. return dataset;
  1516. }
  1517. /**
  1518. * Get shared examples.
  1519. *
  1520. * @param {string} shareToken The share token to get examples for. A share token is the UUID (or LangSmith URL, including UUID) generated when explicitly marking an example as public.
  1521. * @param {Object} [options] Additional options for listing the examples.
  1522. * @param {string[] | undefined} [options.exampleIds] A list of example IDs to filter by.
  1523. * @returns {Promise<Example[]>} The shared examples.
  1524. */
  1525. async listSharedExamples(shareToken, options) {
  1526. const params = {};
  1527. if (options?.exampleIds) {
  1528. params.id = options.exampleIds;
  1529. }
  1530. const urlParams = new URLSearchParams();
  1531. Object.entries(params).forEach(([key, value]) => {
  1532. if (Array.isArray(value)) {
  1533. value.forEach((v) => urlParams.append(key, v));
  1534. }
  1535. else {
  1536. urlParams.append(key, value);
  1537. }
  1538. });
  1539. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/public/${shareToken}/examples?${urlParams.toString()}`, {
  1540. method: "GET",
  1541. headers: this.headers,
  1542. signal: AbortSignal.timeout(this.timeout_ms),
  1543. ...this.fetchOptions,
  1544. });
  1545. const result = await response.json();
  1546. if (!response.ok) {
  1547. if ("detail" in result) {
  1548. throw new Error(`Failed to list shared examples.\nStatus: ${response.status}\nMessage: ${Array.isArray(result.detail)
  1549. ? result.detail.join("\n")
  1550. : "Unspecified error"}`);
  1551. }
  1552. throw new Error(`Failed to list shared examples: ${response.status} ${response.statusText}`);
  1553. }
  1554. return result.map((example) => ({
  1555. ...example,
  1556. _hostUrl: this.getHostUrl(),
  1557. }));
  1558. }
  1559. async createProject({ projectName, description = null, metadata = null, upsert = false, projectExtra = null, referenceDatasetId = null, }) {
  1560. const upsert_ = upsert ? `?upsert=true` : "";
  1561. const endpoint = `${this.apiUrl}/sessions${upsert_}`;
  1562. const extra = projectExtra || {};
  1563. if (metadata) {
  1564. extra["metadata"] = metadata;
  1565. }
  1566. const body = {
  1567. name: projectName,
  1568. extra,
  1569. description,
  1570. };
  1571. if (referenceDatasetId !== null) {
  1572. body["reference_dataset_id"] = referenceDatasetId;
  1573. }
  1574. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), endpoint, {
  1575. method: "POST",
  1576. headers: { ...this.headers, "Content-Type": "application/json" },
  1577. body: JSON.stringify(body),
  1578. signal: AbortSignal.timeout(this.timeout_ms),
  1579. ...this.fetchOptions,
  1580. });
  1581. await (0, error_js_1.raiseForStatus)(response, "create project");
  1582. const result = await response.json();
  1583. return result;
  1584. }
  1585. async updateProject(projectId, { name = null, description = null, metadata = null, projectExtra = null, endTime = null, }) {
  1586. const endpoint = `${this.apiUrl}/sessions/${projectId}`;
  1587. let extra = projectExtra;
  1588. if (metadata) {
  1589. extra = { ...(extra || {}), metadata };
  1590. }
  1591. const body = {
  1592. name,
  1593. extra,
  1594. description,
  1595. end_time: endTime ? new Date(endTime).toISOString() : null,
  1596. };
  1597. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), endpoint, {
  1598. method: "PATCH",
  1599. headers: { ...this.headers, "Content-Type": "application/json" },
  1600. body: JSON.stringify(body),
  1601. signal: AbortSignal.timeout(this.timeout_ms),
  1602. ...this.fetchOptions,
  1603. });
  1604. await (0, error_js_1.raiseForStatus)(response, "update project");
  1605. const result = await response.json();
  1606. return result;
  1607. }
  1608. async hasProject({ projectId, projectName, }) {
  1609. // TODO: Add a head request
  1610. let path = "/sessions";
  1611. const params = new URLSearchParams();
  1612. if (projectId !== undefined && projectName !== undefined) {
  1613. throw new Error("Must provide either projectName or projectId, not both");
  1614. }
  1615. else if (projectId !== undefined) {
  1616. (0, _uuid_js_1.assertUuid)(projectId);
  1617. path += `/${projectId}`;
  1618. }
  1619. else if (projectName !== undefined) {
  1620. params.append("name", projectName);
  1621. }
  1622. else {
  1623. throw new Error("Must provide projectName or projectId");
  1624. }
  1625. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}${path}?${params}`, {
  1626. method: "GET",
  1627. headers: this.headers,
  1628. signal: AbortSignal.timeout(this.timeout_ms),
  1629. ...this.fetchOptions,
  1630. });
  1631. // consume the response body to release the connection
  1632. // https://undici.nodejs.org/#/?id=garbage-collection
  1633. try {
  1634. const result = await response.json();
  1635. if (!response.ok) {
  1636. return false;
  1637. }
  1638. // If it's OK and we're querying by name, need to check the list is not empty
  1639. if (Array.isArray(result)) {
  1640. return result.length > 0;
  1641. }
  1642. // projectId querying
  1643. return true;
  1644. }
  1645. catch (e) {
  1646. return false;
  1647. }
  1648. }
  1649. async readProject({ projectId, projectName, includeStats, }) {
  1650. let path = "/sessions";
  1651. const params = new URLSearchParams();
  1652. if (projectId !== undefined && projectName !== undefined) {
  1653. throw new Error("Must provide either projectName or projectId, not both");
  1654. }
  1655. else if (projectId !== undefined) {
  1656. (0, _uuid_js_1.assertUuid)(projectId);
  1657. path += `/${projectId}`;
  1658. }
  1659. else if (projectName !== undefined) {
  1660. params.append("name", projectName);
  1661. }
  1662. else {
  1663. throw new Error("Must provide projectName or projectId");
  1664. }
  1665. if (includeStats !== undefined) {
  1666. params.append("include_stats", includeStats.toString());
  1667. }
  1668. const response = await this._get(path, params);
  1669. let result;
  1670. if (Array.isArray(response)) {
  1671. if (response.length === 0) {
  1672. throw new Error(`Project[id=${projectId}, name=${projectName}] not found`);
  1673. }
  1674. result = response[0];
  1675. }
  1676. else {
  1677. result = response;
  1678. }
  1679. return result;
  1680. }
  1681. async getProjectUrl({ projectId, projectName, }) {
  1682. if (projectId === undefined && projectName === undefined) {
  1683. throw new Error("Must provide either projectName or projectId");
  1684. }
  1685. const project = await this.readProject({ projectId, projectName });
  1686. const tenantId = await this._getTenantId();
  1687. return `${this.getHostUrl()}/o/${tenantId}/projects/p/${project.id}`;
  1688. }
  1689. async getDatasetUrl({ datasetId, datasetName, }) {
  1690. if (datasetId === undefined && datasetName === undefined) {
  1691. throw new Error("Must provide either datasetName or datasetId");
  1692. }
  1693. const dataset = await this.readDataset({ datasetId, datasetName });
  1694. const tenantId = await this._getTenantId();
  1695. return `${this.getHostUrl()}/o/${tenantId}/datasets/${dataset.id}`;
  1696. }
  1697. async _getTenantId() {
  1698. if (this._tenantId !== null) {
  1699. return this._tenantId;
  1700. }
  1701. const queryParams = new URLSearchParams({ limit: "1" });
  1702. for await (const projects of this._getPaginated("/sessions", queryParams)) {
  1703. this._tenantId = projects[0].tenant_id;
  1704. return projects[0].tenant_id;
  1705. }
  1706. throw new Error("No projects found to resolve tenant.");
  1707. }
  1708. async *listProjects({ projectIds, name, nameContains, referenceDatasetId, referenceDatasetName, referenceFree, metadata, } = {}) {
  1709. const params = new URLSearchParams();
  1710. if (projectIds !== undefined) {
  1711. for (const projectId of projectIds) {
  1712. params.append("id", projectId);
  1713. }
  1714. }
  1715. if (name !== undefined) {
  1716. params.append("name", name);
  1717. }
  1718. if (nameContains !== undefined) {
  1719. params.append("name_contains", nameContains);
  1720. }
  1721. if (referenceDatasetId !== undefined) {
  1722. params.append("reference_dataset", referenceDatasetId);
  1723. }
  1724. else if (referenceDatasetName !== undefined) {
  1725. const dataset = await this.readDataset({
  1726. datasetName: referenceDatasetName,
  1727. });
  1728. params.append("reference_dataset", dataset.id);
  1729. }
  1730. if (referenceFree !== undefined) {
  1731. params.append("reference_free", referenceFree.toString());
  1732. }
  1733. if (metadata !== undefined) {
  1734. params.append("metadata", JSON.stringify(metadata));
  1735. }
  1736. for await (const projects of this._getPaginated("/sessions", params)) {
  1737. yield* projects;
  1738. }
  1739. }
  1740. async deleteProject({ projectId, projectName, }) {
  1741. let projectId_;
  1742. if (projectId === undefined && projectName === undefined) {
  1743. throw new Error("Must provide projectName or projectId");
  1744. }
  1745. else if (projectId !== undefined && projectName !== undefined) {
  1746. throw new Error("Must provide either projectName or projectId, not both");
  1747. }
  1748. else if (projectId === undefined) {
  1749. projectId_ = (await this.readProject({ projectName })).id;
  1750. }
  1751. else {
  1752. projectId_ = projectId;
  1753. }
  1754. (0, _uuid_js_1.assertUuid)(projectId_);
  1755. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/sessions/${projectId_}`, {
  1756. method: "DELETE",
  1757. headers: this.headers,
  1758. signal: AbortSignal.timeout(this.timeout_ms),
  1759. ...this.fetchOptions,
  1760. });
  1761. await (0, error_js_1.raiseForStatus)(response, `delete session ${projectId_} (${projectName})`, true);
  1762. }
  1763. async uploadCsv({ csvFile, fileName, inputKeys, outputKeys, description, dataType, name, }) {
  1764. const url = `${this.apiUrl}/datasets/upload`;
  1765. const formData = new FormData();
  1766. formData.append("file", csvFile, fileName);
  1767. inputKeys.forEach((key) => {
  1768. formData.append("input_keys", key);
  1769. });
  1770. outputKeys.forEach((key) => {
  1771. formData.append("output_keys", key);
  1772. });
  1773. if (description) {
  1774. formData.append("description", description);
  1775. }
  1776. if (dataType) {
  1777. formData.append("data_type", dataType);
  1778. }
  1779. if (name) {
  1780. formData.append("name", name);
  1781. }
  1782. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), url, {
  1783. method: "POST",
  1784. headers: this.headers,
  1785. body: formData,
  1786. signal: AbortSignal.timeout(this.timeout_ms),
  1787. ...this.fetchOptions,
  1788. });
  1789. await (0, error_js_1.raiseForStatus)(response, "upload CSV");
  1790. const result = await response.json();
  1791. return result;
  1792. }
  1793. async createDataset(name, { description, dataType, inputsSchema, outputsSchema, metadata, } = {}) {
  1794. const body = {
  1795. name,
  1796. description,
  1797. extra: metadata ? { metadata } : undefined,
  1798. };
  1799. if (dataType) {
  1800. body.data_type = dataType;
  1801. }
  1802. if (inputsSchema) {
  1803. body.inputs_schema_definition = inputsSchema;
  1804. }
  1805. if (outputsSchema) {
  1806. body.outputs_schema_definition = outputsSchema;
  1807. }
  1808. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/datasets`, {
  1809. method: "POST",
  1810. headers: { ...this.headers, "Content-Type": "application/json" },
  1811. body: JSON.stringify(body),
  1812. signal: AbortSignal.timeout(this.timeout_ms),
  1813. ...this.fetchOptions,
  1814. });
  1815. await (0, error_js_1.raiseForStatus)(response, "create dataset");
  1816. const result = await response.json();
  1817. return result;
  1818. }
  1819. async readDataset({ datasetId, datasetName, }) {
  1820. let path = "/datasets";
  1821. // limit to 1 result
  1822. const params = new URLSearchParams({ limit: "1" });
  1823. if (datasetId !== undefined && datasetName !== undefined) {
  1824. throw new Error("Must provide either datasetName or datasetId, not both");
  1825. }
  1826. else if (datasetId !== undefined) {
  1827. (0, _uuid_js_1.assertUuid)(datasetId);
  1828. path += `/${datasetId}`;
  1829. }
  1830. else if (datasetName !== undefined) {
  1831. params.append("name", datasetName);
  1832. }
  1833. else {
  1834. throw new Error("Must provide datasetName or datasetId");
  1835. }
  1836. const response = await this._get(path, params);
  1837. let result;
  1838. if (Array.isArray(response)) {
  1839. if (response.length === 0) {
  1840. throw new Error(`Dataset[id=${datasetId}, name=${datasetName}] not found`);
  1841. }
  1842. result = response[0];
  1843. }
  1844. else {
  1845. result = response;
  1846. }
  1847. return result;
  1848. }
  1849. async hasDataset({ datasetId, datasetName, }) {
  1850. try {
  1851. await this.readDataset({ datasetId, datasetName });
  1852. return true;
  1853. }
  1854. catch (e) {
  1855. if (
  1856. // eslint-disable-next-line no-instanceof/no-instanceof
  1857. e instanceof Error &&
  1858. e.message.toLocaleLowerCase().includes("not found")) {
  1859. return false;
  1860. }
  1861. throw e;
  1862. }
  1863. }
  1864. async diffDatasetVersions({ datasetId, datasetName, fromVersion, toVersion, }) {
  1865. let datasetId_ = datasetId;
  1866. if (datasetId_ === undefined && datasetName === undefined) {
  1867. throw new Error("Must provide either datasetName or datasetId");
  1868. }
  1869. else if (datasetId_ !== undefined && datasetName !== undefined) {
  1870. throw new Error("Must provide either datasetName or datasetId, not both");
  1871. }
  1872. else if (datasetId_ === undefined) {
  1873. const dataset = await this.readDataset({ datasetName });
  1874. datasetId_ = dataset.id;
  1875. }
  1876. const urlParams = new URLSearchParams({
  1877. from_version: typeof fromVersion === "string"
  1878. ? fromVersion
  1879. : fromVersion.toISOString(),
  1880. to_version: typeof toVersion === "string" ? toVersion : toVersion.toISOString(),
  1881. });
  1882. const response = await this._get(`/datasets/${datasetId_}/versions/diff`, urlParams);
  1883. return response;
  1884. }
  1885. async readDatasetOpenaiFinetuning({ datasetId, datasetName, }) {
  1886. const path = "/datasets";
  1887. if (datasetId !== undefined) {
  1888. // do nothing
  1889. }
  1890. else if (datasetName !== undefined) {
  1891. datasetId = (await this.readDataset({ datasetName })).id;
  1892. }
  1893. else {
  1894. throw new Error("Must provide either datasetName or datasetId");
  1895. }
  1896. const response = await this._getResponse(`${path}/${datasetId}/openai_ft`);
  1897. const datasetText = await response.text();
  1898. const dataset = datasetText
  1899. .trim()
  1900. .split("\n")
  1901. .map((line) => JSON.parse(line));
  1902. return dataset;
  1903. }
  1904. async *listDatasets({ limit = 100, offset = 0, datasetIds, datasetName, datasetNameContains, metadata, } = {}) {
  1905. const path = "/datasets";
  1906. const params = new URLSearchParams({
  1907. limit: limit.toString(),
  1908. offset: offset.toString(),
  1909. });
  1910. if (datasetIds !== undefined) {
  1911. for (const id_ of datasetIds) {
  1912. params.append("id", id_);
  1913. }
  1914. }
  1915. if (datasetName !== undefined) {
  1916. params.append("name", datasetName);
  1917. }
  1918. if (datasetNameContains !== undefined) {
  1919. params.append("name_contains", datasetNameContains);
  1920. }
  1921. if (metadata !== undefined) {
  1922. params.append("metadata", JSON.stringify(metadata));
  1923. }
  1924. for await (const datasets of this._getPaginated(path, params)) {
  1925. yield* datasets;
  1926. }
  1927. }
  1928. /**
  1929. * Update a dataset
  1930. * @param props The dataset details to update
  1931. * @returns The updated dataset
  1932. */
  1933. async updateDataset(props) {
  1934. const { datasetId, datasetName, ...update } = props;
  1935. if (!datasetId && !datasetName) {
  1936. throw new Error("Must provide either datasetName or datasetId");
  1937. }
  1938. const _datasetId = datasetId ?? (await this.readDataset({ datasetName })).id;
  1939. (0, _uuid_js_1.assertUuid)(_datasetId);
  1940. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/datasets/${_datasetId}`, {
  1941. method: "PATCH",
  1942. headers: { ...this.headers, "Content-Type": "application/json" },
  1943. body: JSON.stringify(update),
  1944. signal: AbortSignal.timeout(this.timeout_ms),
  1945. ...this.fetchOptions,
  1946. });
  1947. await (0, error_js_1.raiseForStatus)(response, "update dataset");
  1948. return (await response.json());
  1949. }
  1950. /**
  1951. * Updates a tag on a dataset.
  1952. *
  1953. * If the tag is already assigned to a different version of this dataset,
  1954. * the tag will be moved to the new version. The as_of parameter is used to
  1955. * determine which version of the dataset to apply the new tags to.
  1956. *
  1957. * It must be an exact version of the dataset to succeed. You can
  1958. * use the "readDatasetVersion" method to find the exact version
  1959. * to apply the tags to.
  1960. * @param params.datasetId The ID of the dataset to update. Must be provided if "datasetName" is not provided.
  1961. * @param params.datasetName The name of the dataset to update. Must be provided if "datasetId" is not provided.
  1962. * @param params.asOf The timestamp of the dataset to apply the new tags to.
  1963. * @param params.tag The new tag to apply to the dataset.
  1964. */
  1965. async updateDatasetTag(props) {
  1966. const { datasetId, datasetName, asOf, tag } = props;
  1967. if (!datasetId && !datasetName) {
  1968. throw new Error("Must provide either datasetName or datasetId");
  1969. }
  1970. const _datasetId = datasetId ?? (await this.readDataset({ datasetName })).id;
  1971. (0, _uuid_js_1.assertUuid)(_datasetId);
  1972. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/datasets/${_datasetId}/tags`, {
  1973. method: "PUT",
  1974. headers: { ...this.headers, "Content-Type": "application/json" },
  1975. body: JSON.stringify({
  1976. as_of: typeof asOf === "string" ? asOf : asOf.toISOString(),
  1977. tag,
  1978. }),
  1979. signal: AbortSignal.timeout(this.timeout_ms),
  1980. ...this.fetchOptions,
  1981. });
  1982. await (0, error_js_1.raiseForStatus)(response, "update dataset tags");
  1983. }
  1984. async deleteDataset({ datasetId, datasetName, }) {
  1985. let path = "/datasets";
  1986. let datasetId_ = datasetId;
  1987. if (datasetId !== undefined && datasetName !== undefined) {
  1988. throw new Error("Must provide either datasetName or datasetId, not both");
  1989. }
  1990. else if (datasetName !== undefined) {
  1991. const dataset = await this.readDataset({ datasetName });
  1992. datasetId_ = dataset.id;
  1993. }
  1994. if (datasetId_ !== undefined) {
  1995. (0, _uuid_js_1.assertUuid)(datasetId_);
  1996. path += `/${datasetId_}`;
  1997. }
  1998. else {
  1999. throw new Error("Must provide datasetName or datasetId");
  2000. }
  2001. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), this.apiUrl + path, {
  2002. method: "DELETE",
  2003. headers: this.headers,
  2004. signal: AbortSignal.timeout(this.timeout_ms),
  2005. ...this.fetchOptions,
  2006. });
  2007. await (0, error_js_1.raiseForStatus)(response, `delete ${path}`);
  2008. await response.json();
  2009. }
  2010. async indexDataset({ datasetId, datasetName, tag, }) {
  2011. let datasetId_ = datasetId;
  2012. if (!datasetId_ && !datasetName) {
  2013. throw new Error("Must provide either datasetName or datasetId");
  2014. }
  2015. else if (datasetId_ && datasetName) {
  2016. throw new Error("Must provide either datasetName or datasetId, not both");
  2017. }
  2018. else if (!datasetId_) {
  2019. const dataset = await this.readDataset({ datasetName });
  2020. datasetId_ = dataset.id;
  2021. }
  2022. (0, _uuid_js_1.assertUuid)(datasetId_);
  2023. const data = {
  2024. tag: tag,
  2025. };
  2026. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/datasets/${datasetId_}/index`, {
  2027. method: "POST",
  2028. headers: { ...this.headers, "Content-Type": "application/json" },
  2029. body: JSON.stringify(data),
  2030. signal: AbortSignal.timeout(this.timeout_ms),
  2031. ...this.fetchOptions,
  2032. });
  2033. await (0, error_js_1.raiseForStatus)(response, "index dataset");
  2034. await response.json();
  2035. }
  2036. /**
  2037. * Lets you run a similarity search query on a dataset.
  2038. *
  2039. * Requires the dataset to be indexed. Please see the `indexDataset` method to set up indexing.
  2040. *
  2041. * @param inputs The input on which to run the similarity search. Must have the
  2042. * same schema as the dataset.
  2043. *
  2044. * @param datasetId The dataset to search for similar examples.
  2045. *
  2046. * @param limit The maximum number of examples to return. Will return the top `limit` most
  2047. * similar examples in order of most similar to least similar. If no similar
  2048. * examples are found, random examples will be returned.
  2049. *
  2050. * @param filter A filter string to apply to the search. Only examples will be returned that
  2051. * match the filter string. Some examples of filters
  2052. *
  2053. * - eq(metadata.mykey, "value")
  2054. * - and(neq(metadata.my.nested.key, "value"), neq(metadata.mykey, "value"))
  2055. * - or(eq(metadata.mykey, "value"), eq(metadata.mykey, "othervalue"))
  2056. *
  2057. * @returns A list of similar examples.
  2058. *
  2059. *
  2060. * @example
  2061. * dataset_id = "123e4567-e89b-12d3-a456-426614174000"
  2062. * inputs = {"text": "How many people live in Berlin?"}
  2063. * limit = 5
  2064. * examples = await client.similarExamples(inputs, dataset_id, limit)
  2065. */
  2066. async similarExamples(inputs, datasetId, limit, { filter, } = {}) {
  2067. const data = {
  2068. limit: limit,
  2069. inputs: inputs,
  2070. };
  2071. if (filter !== undefined) {
  2072. data["filter"] = filter;
  2073. }
  2074. (0, _uuid_js_1.assertUuid)(datasetId);
  2075. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/datasets/${datasetId}/search`, {
  2076. method: "POST",
  2077. headers: { ...this.headers, "Content-Type": "application/json" },
  2078. body: JSON.stringify(data),
  2079. signal: AbortSignal.timeout(this.timeout_ms),
  2080. ...this.fetchOptions,
  2081. });
  2082. await (0, error_js_1.raiseForStatus)(response, "fetch similar examples");
  2083. const result = await response.json();
  2084. return result["examples"];
  2085. }
  2086. async createExample(inputsOrUpdate, outputs, options) {
  2087. if (isExampleCreate(inputsOrUpdate)) {
  2088. if (outputs !== undefined || options !== undefined) {
  2089. throw new Error("Cannot provide outputs or options when using ExampleCreate object");
  2090. }
  2091. }
  2092. let datasetId_ = outputs ? options?.datasetId : inputsOrUpdate.dataset_id;
  2093. const datasetName_ = outputs
  2094. ? options?.datasetName
  2095. : inputsOrUpdate.dataset_name;
  2096. if (datasetId_ === undefined && datasetName_ === undefined) {
  2097. throw new Error("Must provide either datasetName or datasetId");
  2098. }
  2099. else if (datasetId_ !== undefined && datasetName_ !== undefined) {
  2100. throw new Error("Must provide either datasetName or datasetId, not both");
  2101. }
  2102. else if (datasetId_ === undefined) {
  2103. const dataset = await this.readDataset({ datasetName: datasetName_ });
  2104. datasetId_ = dataset.id;
  2105. }
  2106. const createdAt_ = (outputs ? options?.createdAt : inputsOrUpdate.created_at) || new Date();
  2107. let data;
  2108. if (!isExampleCreate(inputsOrUpdate)) {
  2109. data = {
  2110. inputs: inputsOrUpdate,
  2111. outputs,
  2112. created_at: createdAt_?.toISOString(),
  2113. id: options?.exampleId,
  2114. metadata: options?.metadata,
  2115. split: options?.split,
  2116. source_run_id: options?.sourceRunId,
  2117. use_source_run_io: options?.useSourceRunIO,
  2118. use_source_run_attachments: options?.useSourceRunAttachments,
  2119. attachments: options?.attachments,
  2120. };
  2121. }
  2122. else {
  2123. data = inputsOrUpdate;
  2124. }
  2125. const response = await this._uploadExamplesMultipart(datasetId_, [data]);
  2126. const example = await this.readExample(response.example_ids?.[0] ?? uuid.v4());
  2127. return example;
  2128. }
  2129. async createExamples(propsOrUploads) {
  2130. if (Array.isArray(propsOrUploads)) {
  2131. if (propsOrUploads.length === 0) {
  2132. return [];
  2133. }
  2134. const uploads = propsOrUploads;
  2135. let datasetId_ = uploads[0].dataset_id;
  2136. const datasetName_ = uploads[0].dataset_name;
  2137. if (datasetId_ === undefined && datasetName_ === undefined) {
  2138. throw new Error("Must provide either datasetName or datasetId");
  2139. }
  2140. else if (datasetId_ !== undefined && datasetName_ !== undefined) {
  2141. throw new Error("Must provide either datasetName or datasetId, not both");
  2142. }
  2143. else if (datasetId_ === undefined) {
  2144. const dataset = await this.readDataset({ datasetName: datasetName_ });
  2145. datasetId_ = dataset.id;
  2146. }
  2147. const response = await this._uploadExamplesMultipart(datasetId_, uploads);
  2148. const examples = await Promise.all(response.example_ids.map((id) => this.readExample(id)));
  2149. return examples;
  2150. }
  2151. const { inputs, outputs, metadata, splits, sourceRunIds, useSourceRunIOs, useSourceRunAttachments, attachments, exampleIds, datasetId, datasetName, } = propsOrUploads;
  2152. if (inputs === undefined) {
  2153. throw new Error("Must provide inputs when using legacy parameters");
  2154. }
  2155. let datasetId_ = datasetId;
  2156. const datasetName_ = datasetName;
  2157. if (datasetId_ === undefined && datasetName_ === undefined) {
  2158. throw new Error("Must provide either datasetName or datasetId");
  2159. }
  2160. else if (datasetId_ !== undefined && datasetName_ !== undefined) {
  2161. throw new Error("Must provide either datasetName or datasetId, not both");
  2162. }
  2163. else if (datasetId_ === undefined) {
  2164. const dataset = await this.readDataset({ datasetName: datasetName_ });
  2165. datasetId_ = dataset.id;
  2166. }
  2167. const formattedExamples = inputs.map((input, idx) => {
  2168. return {
  2169. dataset_id: datasetId_,
  2170. inputs: input,
  2171. outputs: outputs?.[idx],
  2172. metadata: metadata?.[idx],
  2173. split: splits?.[idx],
  2174. id: exampleIds?.[idx],
  2175. attachments: attachments?.[idx],
  2176. source_run_id: sourceRunIds?.[idx],
  2177. use_source_run_io: useSourceRunIOs?.[idx],
  2178. use_source_run_attachments: useSourceRunAttachments?.[idx],
  2179. };
  2180. });
  2181. const response = await this._uploadExamplesMultipart(datasetId_, formattedExamples);
  2182. const examples = await Promise.all(response.example_ids.map((id) => this.readExample(id)));
  2183. return examples;
  2184. }
  2185. async createLLMExample(input, generation, options) {
  2186. return this.createExample({ input }, { output: generation }, options);
  2187. }
  2188. async createChatExample(input, generations, options) {
  2189. const finalInput = input.map((message) => {
  2190. if ((0, messages_js_1.isLangChainMessage)(message)) {
  2191. return (0, messages_js_1.convertLangChainMessageToExample)(message);
  2192. }
  2193. return message;
  2194. });
  2195. const finalOutput = (0, messages_js_1.isLangChainMessage)(generations)
  2196. ? (0, messages_js_1.convertLangChainMessageToExample)(generations)
  2197. : generations;
  2198. return this.createExample({ input: finalInput }, { output: finalOutput }, options);
  2199. }
  2200. async readExample(exampleId) {
  2201. (0, _uuid_js_1.assertUuid)(exampleId);
  2202. const path = `/examples/${exampleId}`;
  2203. const rawExample = await this._get(path);
  2204. const { attachment_urls, ...rest } = rawExample;
  2205. const example = rest;
  2206. if (attachment_urls) {
  2207. example.attachments = Object.entries(attachment_urls).reduce((acc, [key, value]) => {
  2208. acc[key.slice("attachment.".length)] = {
  2209. presigned_url: value.presigned_url,
  2210. mime_type: value.mime_type,
  2211. };
  2212. return acc;
  2213. }, {});
  2214. }
  2215. return example;
  2216. }
  2217. async *listExamples({ datasetId, datasetName, exampleIds, asOf, splits, inlineS3Urls, metadata, limit, offset, filter, includeAttachments, } = {}) {
  2218. let datasetId_;
  2219. if (datasetId !== undefined && datasetName !== undefined) {
  2220. throw new Error("Must provide either datasetName or datasetId, not both");
  2221. }
  2222. else if (datasetId !== undefined) {
  2223. datasetId_ = datasetId;
  2224. }
  2225. else if (datasetName !== undefined) {
  2226. const dataset = await this.readDataset({ datasetName });
  2227. datasetId_ = dataset.id;
  2228. }
  2229. else {
  2230. throw new Error("Must provide a datasetName or datasetId");
  2231. }
  2232. const params = new URLSearchParams({ dataset: datasetId_ });
  2233. const dataset_version = asOf
  2234. ? typeof asOf === "string"
  2235. ? asOf
  2236. : asOf?.toISOString()
  2237. : undefined;
  2238. if (dataset_version) {
  2239. params.append("as_of", dataset_version);
  2240. }
  2241. const inlineS3Urls_ = inlineS3Urls ?? true;
  2242. params.append("inline_s3_urls", inlineS3Urls_.toString());
  2243. if (exampleIds !== undefined) {
  2244. for (const id_ of exampleIds) {
  2245. params.append("id", id_);
  2246. }
  2247. }
  2248. if (splits !== undefined) {
  2249. for (const split of splits) {
  2250. params.append("splits", split);
  2251. }
  2252. }
  2253. if (metadata !== undefined) {
  2254. const serializedMetadata = JSON.stringify(metadata);
  2255. params.append("metadata", serializedMetadata);
  2256. }
  2257. if (limit !== undefined) {
  2258. params.append("limit", limit.toString());
  2259. }
  2260. if (offset !== undefined) {
  2261. params.append("offset", offset.toString());
  2262. }
  2263. if (filter !== undefined) {
  2264. params.append("filter", filter);
  2265. }
  2266. if (includeAttachments === true) {
  2267. ["attachment_urls", "outputs", "metadata"].forEach((field) => params.append("select", field));
  2268. }
  2269. let i = 0;
  2270. for await (const rawExamples of this._getPaginated("/examples", params)) {
  2271. for (const rawExample of rawExamples) {
  2272. const { attachment_urls, ...rest } = rawExample;
  2273. const example = rest;
  2274. if (attachment_urls) {
  2275. example.attachments = Object.entries(attachment_urls).reduce((acc, [key, value]) => {
  2276. acc[key.slice("attachment.".length)] = {
  2277. presigned_url: value.presigned_url,
  2278. mime_type: value.mime_type || undefined,
  2279. };
  2280. return acc;
  2281. }, {});
  2282. }
  2283. yield example;
  2284. i++;
  2285. }
  2286. if (limit !== undefined && i >= limit) {
  2287. break;
  2288. }
  2289. }
  2290. }
  2291. async deleteExample(exampleId) {
  2292. (0, _uuid_js_1.assertUuid)(exampleId);
  2293. const path = `/examples/${exampleId}`;
  2294. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), this.apiUrl + path, {
  2295. method: "DELETE",
  2296. headers: this.headers,
  2297. signal: AbortSignal.timeout(this.timeout_ms),
  2298. ...this.fetchOptions,
  2299. });
  2300. await (0, error_js_1.raiseForStatus)(response, `delete ${path}`);
  2301. await response.json();
  2302. }
  2303. async updateExample(exampleIdOrUpdate, update) {
  2304. let exampleId;
  2305. if (update) {
  2306. exampleId = exampleIdOrUpdate;
  2307. }
  2308. else {
  2309. exampleId = exampleIdOrUpdate.id;
  2310. }
  2311. (0, _uuid_js_1.assertUuid)(exampleId);
  2312. let updateToUse;
  2313. if (update) {
  2314. updateToUse = { id: exampleId, ...update };
  2315. }
  2316. else {
  2317. updateToUse = exampleIdOrUpdate;
  2318. }
  2319. let datasetId;
  2320. if (updateToUse.dataset_id !== undefined) {
  2321. datasetId = updateToUse.dataset_id;
  2322. }
  2323. else {
  2324. const example = await this.readExample(exampleId);
  2325. datasetId = example.dataset_id;
  2326. }
  2327. return this._updateExamplesMultipart(datasetId, [updateToUse]);
  2328. }
  2329. async updateExamples(update) {
  2330. // We will naively get dataset id from first example and assume it works for all
  2331. let datasetId;
  2332. if (update[0].dataset_id === undefined) {
  2333. const example = await this.readExample(update[0].id);
  2334. datasetId = example.dataset_id;
  2335. }
  2336. else {
  2337. datasetId = update[0].dataset_id;
  2338. }
  2339. return this._updateExamplesMultipart(datasetId, update);
  2340. }
  2341. /**
  2342. * Get dataset version by closest date or exact tag.
  2343. *
  2344. * Use this to resolve the nearest version to a given timestamp or for a given tag.
  2345. *
  2346. * @param options The options for getting the dataset version
  2347. * @param options.datasetId The ID of the dataset
  2348. * @param options.datasetName The name of the dataset
  2349. * @param options.asOf The timestamp of the dataset to retrieve
  2350. * @param options.tag The tag of the dataset to retrieve
  2351. * @returns The dataset version
  2352. */
  2353. async readDatasetVersion({ datasetId, datasetName, asOf, tag, }) {
  2354. let resolvedDatasetId;
  2355. if (!datasetId) {
  2356. const dataset = await this.readDataset({ datasetName });
  2357. resolvedDatasetId = dataset.id;
  2358. }
  2359. else {
  2360. resolvedDatasetId = datasetId;
  2361. }
  2362. (0, _uuid_js_1.assertUuid)(resolvedDatasetId);
  2363. if ((asOf && tag) || (!asOf && !tag)) {
  2364. throw new Error("Exactly one of asOf and tag must be specified.");
  2365. }
  2366. const params = new URLSearchParams();
  2367. if (asOf !== undefined) {
  2368. params.append("as_of", typeof asOf === "string" ? asOf : asOf.toISOString());
  2369. }
  2370. if (tag !== undefined) {
  2371. params.append("tag", tag);
  2372. }
  2373. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/datasets/${resolvedDatasetId}/version?${params.toString()}`, {
  2374. method: "GET",
  2375. headers: { ...this.headers },
  2376. signal: AbortSignal.timeout(this.timeout_ms),
  2377. ...this.fetchOptions,
  2378. });
  2379. await (0, error_js_1.raiseForStatus)(response, "read dataset version");
  2380. return await response.json();
  2381. }
  2382. async listDatasetSplits({ datasetId, datasetName, asOf, }) {
  2383. let datasetId_;
  2384. if (datasetId === undefined && datasetName === undefined) {
  2385. throw new Error("Must provide dataset name or ID");
  2386. }
  2387. else if (datasetId !== undefined && datasetName !== undefined) {
  2388. throw new Error("Must provide either datasetName or datasetId, not both");
  2389. }
  2390. else if (datasetId === undefined) {
  2391. const dataset = await this.readDataset({ datasetName });
  2392. datasetId_ = dataset.id;
  2393. }
  2394. else {
  2395. datasetId_ = datasetId;
  2396. }
  2397. (0, _uuid_js_1.assertUuid)(datasetId_);
  2398. const params = new URLSearchParams();
  2399. const dataset_version = asOf
  2400. ? typeof asOf === "string"
  2401. ? asOf
  2402. : asOf?.toISOString()
  2403. : undefined;
  2404. if (dataset_version) {
  2405. params.append("as_of", dataset_version);
  2406. }
  2407. const response = await this._get(`/datasets/${datasetId_}/splits`, params);
  2408. return response;
  2409. }
  2410. async updateDatasetSplits({ datasetId, datasetName, splitName, exampleIds, remove = false, }) {
  2411. let datasetId_;
  2412. if (datasetId === undefined && datasetName === undefined) {
  2413. throw new Error("Must provide dataset name or ID");
  2414. }
  2415. else if (datasetId !== undefined && datasetName !== undefined) {
  2416. throw new Error("Must provide either datasetName or datasetId, not both");
  2417. }
  2418. else if (datasetId === undefined) {
  2419. const dataset = await this.readDataset({ datasetName });
  2420. datasetId_ = dataset.id;
  2421. }
  2422. else {
  2423. datasetId_ = datasetId;
  2424. }
  2425. (0, _uuid_js_1.assertUuid)(datasetId_);
  2426. const data = {
  2427. split_name: splitName,
  2428. examples: exampleIds.map((id) => {
  2429. (0, _uuid_js_1.assertUuid)(id);
  2430. return id;
  2431. }),
  2432. remove,
  2433. };
  2434. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/datasets/${datasetId_}/splits`, {
  2435. method: "PUT",
  2436. headers: { ...this.headers, "Content-Type": "application/json" },
  2437. body: JSON.stringify(data),
  2438. signal: AbortSignal.timeout(this.timeout_ms),
  2439. ...this.fetchOptions,
  2440. });
  2441. await (0, error_js_1.raiseForStatus)(response, "update dataset splits", true);
  2442. }
  2443. /**
  2444. * @deprecated This method is deprecated and will be removed in future LangSmith versions, use `evaluate` from `langsmith/evaluation` instead.
  2445. */
  2446. async evaluateRun(run, evaluator, { sourceInfo, loadChildRuns, referenceExample, } = { loadChildRuns: false }) {
  2447. (0, warn_js_1.warnOnce)("This method is deprecated and will be removed in future LangSmith versions, use `evaluate` from `langsmith/evaluation` instead.");
  2448. let run_;
  2449. if (typeof run === "string") {
  2450. run_ = await this.readRun(run, { loadChildRuns });
  2451. }
  2452. else if (typeof run === "object" && "id" in run) {
  2453. run_ = run;
  2454. }
  2455. else {
  2456. throw new Error(`Invalid run type: ${typeof run}`);
  2457. }
  2458. if (run_.reference_example_id !== null &&
  2459. run_.reference_example_id !== undefined) {
  2460. referenceExample = await this.readExample(run_.reference_example_id);
  2461. }
  2462. const feedbackResult = await evaluator.evaluateRun(run_, referenceExample);
  2463. const [_, feedbacks] = await this._logEvaluationFeedback(feedbackResult, run_, sourceInfo);
  2464. return feedbacks[0];
  2465. }
  2466. async createFeedback(runId, key, { score, value, correction, comment, sourceInfo, feedbackSourceType = "api", sourceRunId, feedbackId, feedbackConfig, projectId, comparativeExperimentId, }) {
  2467. if (!runId && !projectId) {
  2468. throw new Error("One of runId or projectId must be provided");
  2469. }
  2470. if (runId && projectId) {
  2471. throw new Error("Only one of runId or projectId can be provided");
  2472. }
  2473. const feedback_source = {
  2474. type: feedbackSourceType ?? "api",
  2475. metadata: sourceInfo ?? {},
  2476. };
  2477. if (sourceRunId !== undefined &&
  2478. feedback_source?.metadata !== undefined &&
  2479. !feedback_source.metadata["__run"]) {
  2480. feedback_source.metadata["__run"] = { run_id: sourceRunId };
  2481. }
  2482. if (feedback_source?.metadata !== undefined &&
  2483. feedback_source.metadata["__run"]?.run_id !== undefined) {
  2484. (0, _uuid_js_1.assertUuid)(feedback_source.metadata["__run"].run_id);
  2485. }
  2486. const feedback = {
  2487. id: feedbackId ?? uuid.v4(),
  2488. run_id: runId,
  2489. key,
  2490. score: _formatFeedbackScore(score),
  2491. value,
  2492. correction,
  2493. comment,
  2494. feedback_source: feedback_source,
  2495. comparative_experiment_id: comparativeExperimentId,
  2496. feedbackConfig,
  2497. session_id: projectId,
  2498. };
  2499. const url = `${this.apiUrl}/feedback`;
  2500. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), url, {
  2501. method: "POST",
  2502. headers: { ...this.headers, "Content-Type": "application/json" },
  2503. body: JSON.stringify(feedback),
  2504. signal: AbortSignal.timeout(this.timeout_ms),
  2505. ...this.fetchOptions,
  2506. });
  2507. await (0, error_js_1.raiseForStatus)(response, "create feedback", true);
  2508. return feedback;
  2509. }
  2510. async updateFeedback(feedbackId, { score, value, correction, comment, }) {
  2511. const feedbackUpdate = {};
  2512. if (score !== undefined && score !== null) {
  2513. feedbackUpdate["score"] = _formatFeedbackScore(score);
  2514. }
  2515. if (value !== undefined && value !== null) {
  2516. feedbackUpdate["value"] = value;
  2517. }
  2518. if (correction !== undefined && correction !== null) {
  2519. feedbackUpdate["correction"] = correction;
  2520. }
  2521. if (comment !== undefined && comment !== null) {
  2522. feedbackUpdate["comment"] = comment;
  2523. }
  2524. (0, _uuid_js_1.assertUuid)(feedbackId);
  2525. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/feedback/${feedbackId}`, {
  2526. method: "PATCH",
  2527. headers: { ...this.headers, "Content-Type": "application/json" },
  2528. body: JSON.stringify(feedbackUpdate),
  2529. signal: AbortSignal.timeout(this.timeout_ms),
  2530. ...this.fetchOptions,
  2531. });
  2532. await (0, error_js_1.raiseForStatus)(response, "update feedback", true);
  2533. }
  2534. async readFeedback(feedbackId) {
  2535. (0, _uuid_js_1.assertUuid)(feedbackId);
  2536. const path = `/feedback/${feedbackId}`;
  2537. const response = await this._get(path);
  2538. return response;
  2539. }
  2540. async deleteFeedback(feedbackId) {
  2541. (0, _uuid_js_1.assertUuid)(feedbackId);
  2542. const path = `/feedback/${feedbackId}`;
  2543. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), this.apiUrl + path, {
  2544. method: "DELETE",
  2545. headers: this.headers,
  2546. signal: AbortSignal.timeout(this.timeout_ms),
  2547. ...this.fetchOptions,
  2548. });
  2549. await (0, error_js_1.raiseForStatus)(response, `delete ${path}`);
  2550. await response.json();
  2551. }
  2552. async *listFeedback({ runIds, feedbackKeys, feedbackSourceTypes, } = {}) {
  2553. const queryParams = new URLSearchParams();
  2554. if (runIds) {
  2555. queryParams.append("run", runIds.join(","));
  2556. }
  2557. if (feedbackKeys) {
  2558. for (const key of feedbackKeys) {
  2559. queryParams.append("key", key);
  2560. }
  2561. }
  2562. if (feedbackSourceTypes) {
  2563. for (const type of feedbackSourceTypes) {
  2564. queryParams.append("source", type);
  2565. }
  2566. }
  2567. for await (const feedbacks of this._getPaginated("/feedback", queryParams)) {
  2568. yield* feedbacks;
  2569. }
  2570. }
  2571. /**
  2572. * Creates a presigned feedback token and URL.
  2573. *
  2574. * The token can be used to authorize feedback metrics without
  2575. * needing an API key. This is useful for giving browser-based
  2576. * applications the ability to submit feedback without needing
  2577. * to expose an API key.
  2578. *
  2579. * @param runId The ID of the run.
  2580. * @param feedbackKey The feedback key.
  2581. * @param options Additional options for the token.
  2582. * @param options.expiration The expiration time for the token.
  2583. *
  2584. * @returns A promise that resolves to a FeedbackIngestToken.
  2585. */
  2586. async createPresignedFeedbackToken(runId, feedbackKey, { expiration, feedbackConfig, } = {}) {
  2587. const body = {
  2588. run_id: runId,
  2589. feedback_key: feedbackKey,
  2590. feedback_config: feedbackConfig,
  2591. };
  2592. if (expiration) {
  2593. if (typeof expiration === "string") {
  2594. body["expires_at"] = expiration;
  2595. }
  2596. else if (expiration?.hours || expiration?.minutes || expiration?.days) {
  2597. body["expires_in"] = expiration;
  2598. }
  2599. }
  2600. else {
  2601. body["expires_in"] = {
  2602. hours: 3,
  2603. };
  2604. }
  2605. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/feedback/tokens`, {
  2606. method: "POST",
  2607. headers: { ...this.headers, "Content-Type": "application/json" },
  2608. body: JSON.stringify(body),
  2609. signal: AbortSignal.timeout(this.timeout_ms),
  2610. ...this.fetchOptions,
  2611. });
  2612. const result = await response.json();
  2613. return result;
  2614. }
  2615. async createComparativeExperiment({ name, experimentIds, referenceDatasetId, createdAt, description, metadata, id, }) {
  2616. if (experimentIds.length === 0) {
  2617. throw new Error("At least one experiment is required");
  2618. }
  2619. if (!referenceDatasetId) {
  2620. referenceDatasetId = (await this.readProject({
  2621. projectId: experimentIds[0],
  2622. })).reference_dataset_id;
  2623. }
  2624. if (!referenceDatasetId == null) {
  2625. throw new Error("A reference dataset is required");
  2626. }
  2627. const body = {
  2628. id,
  2629. name,
  2630. experiment_ids: experimentIds,
  2631. reference_dataset_id: referenceDatasetId,
  2632. description,
  2633. created_at: (createdAt ?? new Date())?.toISOString(),
  2634. extra: {},
  2635. };
  2636. if (metadata)
  2637. body.extra["metadata"] = metadata;
  2638. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/datasets/comparative`, {
  2639. method: "POST",
  2640. headers: { ...this.headers, "Content-Type": "application/json" },
  2641. body: JSON.stringify(body),
  2642. signal: AbortSignal.timeout(this.timeout_ms),
  2643. ...this.fetchOptions,
  2644. });
  2645. return await response.json();
  2646. }
  2647. /**
  2648. * Retrieves a list of presigned feedback tokens for a given run ID.
  2649. * @param runId The ID of the run.
  2650. * @returns An async iterable of FeedbackIngestToken objects.
  2651. */
  2652. async *listPresignedFeedbackTokens(runId) {
  2653. (0, _uuid_js_1.assertUuid)(runId);
  2654. const params = new URLSearchParams({ run_id: runId });
  2655. for await (const tokens of this._getPaginated("/feedback/tokens", params)) {
  2656. yield* tokens;
  2657. }
  2658. }
  2659. _selectEvalResults(results) {
  2660. let results_;
  2661. if ("results" in results) {
  2662. results_ = results.results;
  2663. }
  2664. else if (Array.isArray(results)) {
  2665. results_ = results;
  2666. }
  2667. else {
  2668. results_ = [results];
  2669. }
  2670. return results_;
  2671. }
  2672. async _logEvaluationFeedback(evaluatorResponse, run, sourceInfo) {
  2673. const evalResults = this._selectEvalResults(evaluatorResponse);
  2674. const feedbacks = [];
  2675. for (const res of evalResults) {
  2676. let sourceInfo_ = sourceInfo || {};
  2677. if (res.evaluatorInfo) {
  2678. sourceInfo_ = { ...res.evaluatorInfo, ...sourceInfo_ };
  2679. }
  2680. let runId_ = null;
  2681. if (res.targetRunId) {
  2682. runId_ = res.targetRunId;
  2683. }
  2684. else if (run) {
  2685. runId_ = run.id;
  2686. }
  2687. feedbacks.push(await this.createFeedback(runId_, res.key, {
  2688. score: res.score,
  2689. value: res.value,
  2690. comment: res.comment,
  2691. correction: res.correction,
  2692. sourceInfo: sourceInfo_,
  2693. sourceRunId: res.sourceRunId,
  2694. feedbackConfig: res.feedbackConfig,
  2695. feedbackSourceType: "model",
  2696. }));
  2697. }
  2698. return [evalResults, feedbacks];
  2699. }
  2700. async logEvaluationFeedback(evaluatorResponse, run, sourceInfo) {
  2701. const [results] = await this._logEvaluationFeedback(evaluatorResponse, run, sourceInfo);
  2702. return results;
  2703. }
  2704. /**
  2705. * API for managing annotation queues
  2706. */
  2707. /**
  2708. * List the annotation queues on the LangSmith API.
  2709. * @param options - The options for listing annotation queues
  2710. * @param options.queueIds - The IDs of the queues to filter by
  2711. * @param options.name - The name of the queue to filter by
  2712. * @param options.nameContains - The substring that the queue name should contain
  2713. * @param options.limit - The maximum number of queues to return
  2714. * @returns An iterator of AnnotationQueue objects
  2715. */
  2716. async *listAnnotationQueues(options = {}) {
  2717. const { queueIds, name, nameContains, limit } = options;
  2718. const params = new URLSearchParams();
  2719. if (queueIds) {
  2720. queueIds.forEach((id, i) => {
  2721. (0, _uuid_js_1.assertUuid)(id, `queueIds[${i}]`);
  2722. params.append("ids", id);
  2723. });
  2724. }
  2725. if (name)
  2726. params.append("name", name);
  2727. if (nameContains)
  2728. params.append("name_contains", nameContains);
  2729. params.append("limit", (limit !== undefined ? Math.min(limit, 100) : 100).toString());
  2730. let count = 0;
  2731. for await (const queues of this._getPaginated("/annotation-queues", params)) {
  2732. yield* queues;
  2733. count++;
  2734. if (limit !== undefined && count >= limit)
  2735. break;
  2736. }
  2737. }
  2738. /**
  2739. * Create an annotation queue on the LangSmith API.
  2740. * @param options - The options for creating an annotation queue
  2741. * @param options.name - The name of the annotation queue
  2742. * @param options.description - The description of the annotation queue
  2743. * @param options.queueId - The ID of the annotation queue
  2744. * @returns The created AnnotationQueue object
  2745. */
  2746. async createAnnotationQueue(options) {
  2747. const { name, description, queueId, rubricInstructions } = options;
  2748. const body = {
  2749. name,
  2750. description,
  2751. id: queueId || uuid.v4(),
  2752. rubric_instructions: rubricInstructions,
  2753. };
  2754. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/annotation-queues`, {
  2755. method: "POST",
  2756. headers: { ...this.headers, "Content-Type": "application/json" },
  2757. body: JSON.stringify(Object.fromEntries(Object.entries(body).filter(([_, v]) => v !== undefined))),
  2758. signal: AbortSignal.timeout(this.timeout_ms),
  2759. ...this.fetchOptions,
  2760. });
  2761. await (0, error_js_1.raiseForStatus)(response, "create annotation queue");
  2762. const data = await response.json();
  2763. return data;
  2764. }
  2765. /**
  2766. * Read an annotation queue with the specified queue ID.
  2767. * @param queueId - The ID of the annotation queue to read
  2768. * @returns The AnnotationQueueWithDetails object
  2769. */
  2770. async readAnnotationQueue(queueId) {
  2771. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/annotation-queues/${(0, _uuid_js_1.assertUuid)(queueId, "queueId")}`, {
  2772. method: "GET",
  2773. headers: this.headers,
  2774. signal: AbortSignal.timeout(this.timeout_ms),
  2775. ...this.fetchOptions,
  2776. });
  2777. await (0, error_js_1.raiseForStatus)(response, "read annotation queue");
  2778. const data = await response.json();
  2779. return data;
  2780. }
  2781. /**
  2782. * Update an annotation queue with the specified queue ID.
  2783. * @param queueId - The ID of the annotation queue to update
  2784. * @param options - The options for updating the annotation queue
  2785. * @param options.name - The new name for the annotation queue
  2786. * @param options.description - The new description for the annotation queue
  2787. */
  2788. async updateAnnotationQueue(queueId, options) {
  2789. const { name, description, rubricInstructions } = options;
  2790. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/annotation-queues/${(0, _uuid_js_1.assertUuid)(queueId, "queueId")}`, {
  2791. method: "PATCH",
  2792. headers: { ...this.headers, "Content-Type": "application/json" },
  2793. body: JSON.stringify({
  2794. name,
  2795. description,
  2796. rubric_instructions: rubricInstructions,
  2797. }),
  2798. signal: AbortSignal.timeout(this.timeout_ms),
  2799. ...this.fetchOptions,
  2800. });
  2801. await (0, error_js_1.raiseForStatus)(response, "update annotation queue");
  2802. }
  2803. /**
  2804. * Delete an annotation queue with the specified queue ID.
  2805. * @param queueId - The ID of the annotation queue to delete
  2806. */
  2807. async deleteAnnotationQueue(queueId) {
  2808. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/annotation-queues/${(0, _uuid_js_1.assertUuid)(queueId, "queueId")}`, {
  2809. method: "DELETE",
  2810. headers: { ...this.headers, Accept: "application/json" },
  2811. signal: AbortSignal.timeout(this.timeout_ms),
  2812. ...this.fetchOptions,
  2813. });
  2814. await (0, error_js_1.raiseForStatus)(response, "delete annotation queue");
  2815. }
  2816. /**
  2817. * Add runs to an annotation queue with the specified queue ID.
  2818. * @param queueId - The ID of the annotation queue
  2819. * @param runIds - The IDs of the runs to be added to the annotation queue
  2820. */
  2821. async addRunsToAnnotationQueue(queueId, runIds) {
  2822. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/annotation-queues/${(0, _uuid_js_1.assertUuid)(queueId, "queueId")}/runs`, {
  2823. method: "POST",
  2824. headers: { ...this.headers, "Content-Type": "application/json" },
  2825. body: JSON.stringify(runIds.map((id, i) => (0, _uuid_js_1.assertUuid)(id, `runIds[${i}]`).toString())),
  2826. signal: AbortSignal.timeout(this.timeout_ms),
  2827. ...this.fetchOptions,
  2828. });
  2829. await (0, error_js_1.raiseForStatus)(response, "add runs to annotation queue");
  2830. }
  2831. /**
  2832. * Get a run from an annotation queue at the specified index.
  2833. * @param queueId - The ID of the annotation queue
  2834. * @param index - The index of the run to retrieve
  2835. * @returns A Promise that resolves to a RunWithAnnotationQueueInfo object
  2836. * @throws {Error} If the run is not found at the given index or for other API-related errors
  2837. */
  2838. async getRunFromAnnotationQueue(queueId, index) {
  2839. const baseUrl = `/annotation-queues/${(0, _uuid_js_1.assertUuid)(queueId, "queueId")}/run`;
  2840. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}${baseUrl}/${index}`, {
  2841. method: "GET",
  2842. headers: this.headers,
  2843. signal: AbortSignal.timeout(this.timeout_ms),
  2844. ...this.fetchOptions,
  2845. });
  2846. await (0, error_js_1.raiseForStatus)(response, "get run from annotation queue");
  2847. return await response.json();
  2848. }
  2849. /**
  2850. * Delete a run from an an annotation queue.
  2851. * @param queueId - The ID of the annotation queue to delete the run from
  2852. * @param queueRunId - The ID of the run to delete from the annotation queue
  2853. */
  2854. async deleteRunFromAnnotationQueue(queueId, queueRunId) {
  2855. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/annotation-queues/${(0, _uuid_js_1.assertUuid)(queueId, "queueId")}/runs/${(0, _uuid_js_1.assertUuid)(queueRunId, "queueRunId")}`, {
  2856. method: "DELETE",
  2857. headers: { ...this.headers, Accept: "application/json" },
  2858. signal: AbortSignal.timeout(this.timeout_ms),
  2859. ...this.fetchOptions,
  2860. });
  2861. await (0, error_js_1.raiseForStatus)(response, "delete run from annotation queue");
  2862. }
  2863. /**
  2864. * Get the size of an annotation queue.
  2865. * @param queueId - The ID of the annotation queue
  2866. */
  2867. async getSizeFromAnnotationQueue(queueId) {
  2868. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/annotation-queues/${(0, _uuid_js_1.assertUuid)(queueId, "queueId")}/size`, {
  2869. method: "GET",
  2870. headers: this.headers,
  2871. signal: AbortSignal.timeout(this.timeout_ms),
  2872. ...this.fetchOptions,
  2873. });
  2874. await (0, error_js_1.raiseForStatus)(response, "get size from annotation queue");
  2875. return await response.json();
  2876. }
  2877. async _currentTenantIsOwner(owner) {
  2878. const settings = await this._getSettings();
  2879. return owner == "-" || settings.tenant_handle === owner;
  2880. }
  2881. async _ownerConflictError(action, owner) {
  2882. const settings = await this._getSettings();
  2883. return new Error(`Cannot ${action} for another tenant.\n
  2884. Current tenant: ${settings.tenant_handle}\n
  2885. Requested tenant: ${owner}`);
  2886. }
  2887. async _getLatestCommitHash(promptOwnerAndName) {
  2888. const res = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/commits/${promptOwnerAndName}/?limit=${1}&offset=${0}`, {
  2889. method: "GET",
  2890. headers: this.headers,
  2891. signal: AbortSignal.timeout(this.timeout_ms),
  2892. ...this.fetchOptions,
  2893. });
  2894. const json = await res.json();
  2895. if (!res.ok) {
  2896. const detail = typeof json.detail === "string"
  2897. ? json.detail
  2898. : JSON.stringify(json.detail);
  2899. const error = new Error(`Error ${res.status}: ${res.statusText}\n${detail}`);
  2900. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  2901. error.statusCode = res.status;
  2902. throw error;
  2903. }
  2904. if (json.commits.length === 0) {
  2905. return undefined;
  2906. }
  2907. return json.commits[0].commit_hash;
  2908. }
  2909. async _likeOrUnlikePrompt(promptIdentifier, like) {
  2910. const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
  2911. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/likes/${owner}/${promptName}`, {
  2912. method: "POST",
  2913. body: JSON.stringify({ like: like }),
  2914. headers: { ...this.headers, "Content-Type": "application/json" },
  2915. signal: AbortSignal.timeout(this.timeout_ms),
  2916. ...this.fetchOptions,
  2917. });
  2918. await (0, error_js_1.raiseForStatus)(response, `${like ? "like" : "unlike"} prompt`);
  2919. return await response.json();
  2920. }
  2921. async _getPromptUrl(promptIdentifier) {
  2922. const [owner, promptName, commitHash] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
  2923. if (!(await this._currentTenantIsOwner(owner))) {
  2924. if (commitHash !== "latest") {
  2925. return `${this.getHostUrl()}/hub/${owner}/${promptName}/${commitHash.substring(0, 8)}`;
  2926. }
  2927. else {
  2928. return `${this.getHostUrl()}/hub/${owner}/${promptName}`;
  2929. }
  2930. }
  2931. else {
  2932. const settings = await this._getSettings();
  2933. if (commitHash !== "latest") {
  2934. return `${this.getHostUrl()}/prompts/${promptName}/${commitHash.substring(0, 8)}?organizationId=${settings.id}`;
  2935. }
  2936. else {
  2937. return `${this.getHostUrl()}/prompts/${promptName}?organizationId=${settings.id}`;
  2938. }
  2939. }
  2940. }
  2941. async promptExists(promptIdentifier) {
  2942. const prompt = await this.getPrompt(promptIdentifier);
  2943. return !!prompt;
  2944. }
  2945. async likePrompt(promptIdentifier) {
  2946. return this._likeOrUnlikePrompt(promptIdentifier, true);
  2947. }
  2948. async unlikePrompt(promptIdentifier) {
  2949. return this._likeOrUnlikePrompt(promptIdentifier, false);
  2950. }
  2951. async *listCommits(promptOwnerAndName) {
  2952. for await (const commits of this._getPaginated(`/commits/${promptOwnerAndName}/`, new URLSearchParams(), (res) => res.commits)) {
  2953. yield* commits;
  2954. }
  2955. }
  2956. async *listPrompts(options) {
  2957. const params = new URLSearchParams();
  2958. params.append("sort_field", options?.sortField ?? "updated_at");
  2959. params.append("sort_direction", "desc");
  2960. params.append("is_archived", (!!options?.isArchived).toString());
  2961. if (options?.isPublic !== undefined) {
  2962. params.append("is_public", options.isPublic.toString());
  2963. }
  2964. if (options?.query) {
  2965. params.append("query", options.query);
  2966. }
  2967. for await (const prompts of this._getPaginated("/repos", params, (res) => res.repos)) {
  2968. yield* prompts;
  2969. }
  2970. }
  2971. async getPrompt(promptIdentifier) {
  2972. const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
  2973. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/repos/${owner}/${promptName}`, {
  2974. method: "GET",
  2975. headers: this.headers,
  2976. signal: AbortSignal.timeout(this.timeout_ms),
  2977. ...this.fetchOptions,
  2978. });
  2979. if (response.status === 404) {
  2980. return null;
  2981. }
  2982. await (0, error_js_1.raiseForStatus)(response, "get prompt");
  2983. const result = await response.json();
  2984. if (result.repo) {
  2985. return result.repo;
  2986. }
  2987. else {
  2988. return null;
  2989. }
  2990. }
  2991. async createPrompt(promptIdentifier, options) {
  2992. const settings = await this._getSettings();
  2993. if (options?.isPublic && !settings.tenant_handle) {
  2994. throw new Error(`Cannot create a public prompt without first\n
  2995. creating a LangChain Hub handle.
  2996. You can add a handle by creating a public prompt at:\n
  2997. https://smith.langchain.com/prompts`);
  2998. }
  2999. const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
  3000. if (!(await this._currentTenantIsOwner(owner))) {
  3001. throw await this._ownerConflictError("create a prompt", owner);
  3002. }
  3003. const data = {
  3004. repo_handle: promptName,
  3005. ...(options?.description && { description: options.description }),
  3006. ...(options?.readme && { readme: options.readme }),
  3007. ...(options?.tags && { tags: options.tags }),
  3008. is_public: !!options?.isPublic,
  3009. };
  3010. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/repos/`, {
  3011. method: "POST",
  3012. headers: { ...this.headers, "Content-Type": "application/json" },
  3013. body: JSON.stringify(data),
  3014. signal: AbortSignal.timeout(this.timeout_ms),
  3015. ...this.fetchOptions,
  3016. });
  3017. await (0, error_js_1.raiseForStatus)(response, "create prompt");
  3018. const { repo } = await response.json();
  3019. return repo;
  3020. }
  3021. async createCommit(promptIdentifier, object, options) {
  3022. if (!(await this.promptExists(promptIdentifier))) {
  3023. throw new Error("Prompt does not exist, you must create it first.");
  3024. }
  3025. const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
  3026. const resolvedParentCommitHash = options?.parentCommitHash === "latest" || !options?.parentCommitHash
  3027. ? await this._getLatestCommitHash(`${owner}/${promptName}`)
  3028. : options?.parentCommitHash;
  3029. const payload = {
  3030. manifest: JSON.parse(JSON.stringify(object)),
  3031. parent_commit: resolvedParentCommitHash,
  3032. };
  3033. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/commits/${owner}/${promptName}`, {
  3034. method: "POST",
  3035. headers: { ...this.headers, "Content-Type": "application/json" },
  3036. body: JSON.stringify(payload),
  3037. signal: AbortSignal.timeout(this.timeout_ms),
  3038. ...this.fetchOptions,
  3039. });
  3040. await (0, error_js_1.raiseForStatus)(response, "create commit");
  3041. const result = await response.json();
  3042. return this._getPromptUrl(`${owner}/${promptName}${result.commit_hash ? `:${result.commit_hash}` : ""}`);
  3043. }
  3044. /**
  3045. * Update examples with attachments using multipart form data.
  3046. * @param updates List of ExampleUpdateWithAttachments objects to upsert
  3047. * @returns Promise with the update response
  3048. */
  3049. async updateExamplesMultipart(datasetId, updates = []) {
  3050. return this._updateExamplesMultipart(datasetId, updates);
  3051. }
  3052. async _updateExamplesMultipart(datasetId, updates = []) {
  3053. if (!(await this._getMultiPartSupport())) {
  3054. throw new Error("Your LangSmith deployment does not allow using the multipart examples endpoint, please upgrade your deployment to the latest version.");
  3055. }
  3056. const formData = new FormData();
  3057. for (const example of updates) {
  3058. const exampleId = example.id;
  3059. // Prepare the main example body
  3060. const exampleBody = {
  3061. ...(example.metadata && { metadata: example.metadata }),
  3062. ...(example.split && { split: example.split }),
  3063. };
  3064. // Add main example data
  3065. const stringifiedExample = (0, index_js_2.serialize)(exampleBody, `Serializing body for example with id: ${exampleId}`);
  3066. const exampleBlob = new Blob([stringifiedExample], {
  3067. type: "application/json",
  3068. });
  3069. formData.append(exampleId, exampleBlob);
  3070. // Add inputs if present
  3071. if (example.inputs) {
  3072. const stringifiedInputs = (0, index_js_2.serialize)(example.inputs, `Serializing inputs for example with id: ${exampleId}`);
  3073. const inputsBlob = new Blob([stringifiedInputs], {
  3074. type: "application/json",
  3075. });
  3076. formData.append(`${exampleId}.inputs`, inputsBlob);
  3077. }
  3078. // Add outputs if present
  3079. if (example.outputs) {
  3080. const stringifiedOutputs = (0, index_js_2.serialize)(example.outputs, `Serializing outputs whle updating example with id: ${exampleId}`);
  3081. const outputsBlob = new Blob([stringifiedOutputs], {
  3082. type: "application/json",
  3083. });
  3084. formData.append(`${exampleId}.outputs`, outputsBlob);
  3085. }
  3086. // Add attachments if present
  3087. if (example.attachments) {
  3088. for (const [name, attachment] of Object.entries(example.attachments)) {
  3089. let mimeType;
  3090. let data;
  3091. if (Array.isArray(attachment)) {
  3092. [mimeType, data] = attachment;
  3093. }
  3094. else {
  3095. mimeType = attachment.mimeType;
  3096. data = attachment.data;
  3097. }
  3098. const attachmentBlob = new Blob([data], {
  3099. type: `${mimeType}; length=${data.byteLength}`,
  3100. });
  3101. formData.append(`${exampleId}.attachment.${name}`, attachmentBlob);
  3102. }
  3103. }
  3104. if (example.attachments_operations) {
  3105. const stringifiedAttachmentsOperations = (0, index_js_2.serialize)(example.attachments_operations, `Serializing attachments while updating example with id: ${exampleId}`);
  3106. const attachmentsOperationsBlob = new Blob([stringifiedAttachmentsOperations], {
  3107. type: "application/json",
  3108. });
  3109. formData.append(`${exampleId}.attachments_operations`, attachmentsOperationsBlob);
  3110. }
  3111. }
  3112. const datasetIdToUse = datasetId ?? updates[0]?.dataset_id;
  3113. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/v1/platform/datasets/${datasetIdToUse}/examples`, {
  3114. method: "PATCH",
  3115. headers: this.headers,
  3116. body: formData,
  3117. });
  3118. const result = await response.json();
  3119. return result;
  3120. }
  3121. /**
  3122. * Upload examples with attachments using multipart form data.
  3123. * @param uploads List of ExampleUploadWithAttachments objects to upload
  3124. * @returns Promise with the upload response
  3125. * @deprecated This method is deprecated and will be removed in future LangSmith versions, please use `createExamples` instead
  3126. */
  3127. async uploadExamplesMultipart(datasetId, uploads = []) {
  3128. return this._uploadExamplesMultipart(datasetId, uploads);
  3129. }
  3130. async _uploadExamplesMultipart(datasetId, uploads = []) {
  3131. if (!(await this._getMultiPartSupport())) {
  3132. throw new Error("Your LangSmith deployment does not allow using the multipart examples endpoint, please upgrade your deployment to the latest version.");
  3133. }
  3134. const formData = new FormData();
  3135. for (const example of uploads) {
  3136. const exampleId = (example.id ?? uuid.v4()).toString();
  3137. // Prepare the main example body
  3138. const exampleBody = {
  3139. created_at: example.created_at,
  3140. ...(example.metadata && { metadata: example.metadata }),
  3141. ...(example.split && { split: example.split }),
  3142. ...(example.source_run_id && { source_run_id: example.source_run_id }),
  3143. ...(example.use_source_run_io && {
  3144. use_source_run_io: example.use_source_run_io,
  3145. }),
  3146. ...(example.use_source_run_attachments && {
  3147. use_source_run_attachments: example.use_source_run_attachments,
  3148. }),
  3149. };
  3150. // Add main example data
  3151. const stringifiedExample = (0, index_js_2.serialize)(exampleBody, `Serializing body for uploaded example with id: ${exampleId}`);
  3152. const exampleBlob = new Blob([stringifiedExample], {
  3153. type: "application/json",
  3154. });
  3155. formData.append(exampleId, exampleBlob);
  3156. // Add inputs if present
  3157. if (example.inputs) {
  3158. const stringifiedInputs = (0, index_js_2.serialize)(example.inputs, `Serializing inputs for uploaded example with id: ${exampleId}`);
  3159. const inputsBlob = new Blob([stringifiedInputs], {
  3160. type: "application/json",
  3161. });
  3162. formData.append(`${exampleId}.inputs`, inputsBlob);
  3163. }
  3164. // Add outputs if present
  3165. if (example.outputs) {
  3166. const stringifiedOutputs = (0, index_js_2.serialize)(example.outputs, `Serializing outputs for uploaded example with id: ${exampleId}`);
  3167. const outputsBlob = new Blob([stringifiedOutputs], {
  3168. type: "application/json",
  3169. });
  3170. formData.append(`${exampleId}.outputs`, outputsBlob);
  3171. }
  3172. // Add attachments if present
  3173. if (example.attachments) {
  3174. for (const [name, attachment] of Object.entries(example.attachments)) {
  3175. let mimeType;
  3176. let data;
  3177. if (Array.isArray(attachment)) {
  3178. [mimeType, data] = attachment;
  3179. }
  3180. else {
  3181. mimeType = attachment.mimeType;
  3182. data = attachment.data;
  3183. }
  3184. const attachmentBlob = new Blob([data], {
  3185. type: `${mimeType}; length=${data.byteLength}`,
  3186. });
  3187. formData.append(`${exampleId}.attachment.${name}`, attachmentBlob);
  3188. }
  3189. }
  3190. }
  3191. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/v1/platform/datasets/${datasetId}/examples`, {
  3192. method: "POST",
  3193. headers: this.headers,
  3194. body: formData,
  3195. });
  3196. await (0, error_js_1.raiseForStatus)(response, "upload examples");
  3197. const result = await response.json();
  3198. return result;
  3199. }
  3200. async updatePrompt(promptIdentifier, options) {
  3201. if (!(await this.promptExists(promptIdentifier))) {
  3202. throw new Error("Prompt does not exist, you must create it first.");
  3203. }
  3204. const [owner, promptName] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
  3205. if (!(await this._currentTenantIsOwner(owner))) {
  3206. throw await this._ownerConflictError("update a prompt", owner);
  3207. }
  3208. const payload = {};
  3209. if (options?.description !== undefined)
  3210. payload.description = options.description;
  3211. if (options?.readme !== undefined)
  3212. payload.readme = options.readme;
  3213. if (options?.tags !== undefined)
  3214. payload.tags = options.tags;
  3215. if (options?.isPublic !== undefined)
  3216. payload.is_public = options.isPublic;
  3217. if (options?.isArchived !== undefined)
  3218. payload.is_archived = options.isArchived;
  3219. // Check if payload is empty
  3220. if (Object.keys(payload).length === 0) {
  3221. throw new Error("No valid update options provided");
  3222. }
  3223. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/repos/${owner}/${promptName}`, {
  3224. method: "PATCH",
  3225. body: JSON.stringify(payload),
  3226. headers: {
  3227. ...this.headers,
  3228. "Content-Type": "application/json",
  3229. },
  3230. signal: AbortSignal.timeout(this.timeout_ms),
  3231. ...this.fetchOptions,
  3232. });
  3233. await (0, error_js_1.raiseForStatus)(response, "update prompt");
  3234. return response.json();
  3235. }
  3236. async deletePrompt(promptIdentifier) {
  3237. if (!(await this.promptExists(promptIdentifier))) {
  3238. throw new Error("Prompt does not exist, you must create it first.");
  3239. }
  3240. const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
  3241. if (!(await this._currentTenantIsOwner(owner))) {
  3242. throw await this._ownerConflictError("delete a prompt", owner);
  3243. }
  3244. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/repos/${owner}/${promptName}`, {
  3245. method: "DELETE",
  3246. headers: this.headers,
  3247. signal: AbortSignal.timeout(this.timeout_ms),
  3248. ...this.fetchOptions,
  3249. });
  3250. return await response.json();
  3251. }
  3252. async pullPromptCommit(promptIdentifier, options) {
  3253. const [owner, promptName, commitHash] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
  3254. const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/commits/${owner}/${promptName}/${commitHash}${options?.includeModel ? "?include_model=true" : ""}`, {
  3255. method: "GET",
  3256. headers: this.headers,
  3257. signal: AbortSignal.timeout(this.timeout_ms),
  3258. ...this.fetchOptions,
  3259. });
  3260. await (0, error_js_1.raiseForStatus)(response, "pull prompt commit");
  3261. const result = await response.json();
  3262. return {
  3263. owner,
  3264. repo: promptName,
  3265. commit_hash: result.commit_hash,
  3266. manifest: result.manifest,
  3267. examples: result.examples,
  3268. };
  3269. }
  3270. /**
  3271. * This method should not be used directly, use `import { pull } from "langchain/hub"` instead.
  3272. * Using this method directly returns the JSON string of the prompt rather than a LangChain object.
  3273. * @private
  3274. */
  3275. async _pullPrompt(promptIdentifier, options) {
  3276. const promptObject = await this.pullPromptCommit(promptIdentifier, {
  3277. includeModel: options?.includeModel,
  3278. });
  3279. const prompt = JSON.stringify(promptObject.manifest);
  3280. return prompt;
  3281. }
  3282. async pushPrompt(promptIdentifier, options) {
  3283. // Create or update prompt metadata
  3284. if (await this.promptExists(promptIdentifier)) {
  3285. if (options && Object.keys(options).some((key) => key !== "object")) {
  3286. await this.updatePrompt(promptIdentifier, {
  3287. description: options?.description,
  3288. readme: options?.readme,
  3289. tags: options?.tags,
  3290. isPublic: options?.isPublic,
  3291. });
  3292. }
  3293. }
  3294. else {
  3295. await this.createPrompt(promptIdentifier, {
  3296. description: options?.description,
  3297. readme: options?.readme,
  3298. tags: options?.tags,
  3299. isPublic: options?.isPublic,
  3300. });
  3301. }
  3302. if (!options?.object) {
  3303. return await this._getPromptUrl(promptIdentifier);
  3304. }
  3305. // Create a commit with the new manifest
  3306. const url = await this.createCommit(promptIdentifier, options?.object, {
  3307. parentCommitHash: options?.parentCommitHash,
  3308. });
  3309. return url;
  3310. }
  3311. /**
  3312. * Clone a public dataset to your own langsmith tenant.
  3313. * This operation is idempotent. If you already have a dataset with the given name,
  3314. * this function will do nothing.
  3315. * @param {string} tokenOrUrl The token of the public dataset to clone.
  3316. * @param {Object} [options] Additional options for cloning the dataset.
  3317. * @param {string} [options.sourceApiUrl] The URL of the langsmith server where the data is hosted. Defaults to the API URL of your current client.
  3318. * @param {string} [options.datasetName] The name of the dataset to create in your tenant. Defaults to the name of the public dataset.
  3319. * @returns {Promise<void>}
  3320. */
  3321. async clonePublicDataset(tokenOrUrl, options = {}) {
  3322. const { sourceApiUrl = this.apiUrl, datasetName } = options;
  3323. const [parsedApiUrl, tokenUuid] = this.parseTokenOrUrl(tokenOrUrl, sourceApiUrl);
  3324. const sourceClient = new Client({
  3325. apiUrl: parsedApiUrl,
  3326. // Placeholder API key not needed anymore in most cases, but
  3327. // some private deployments may have API key-based rate limiting
  3328. // that would cause this to fail if we provide no value.
  3329. apiKey: "placeholder",
  3330. });
  3331. const ds = await sourceClient.readSharedDataset(tokenUuid);
  3332. const finalDatasetName = datasetName || ds.name;
  3333. try {
  3334. if (await this.hasDataset({ datasetId: finalDatasetName })) {
  3335. console.log(`Dataset ${finalDatasetName} already exists in your tenant. Skipping.`);
  3336. return;
  3337. }
  3338. }
  3339. catch (_) {
  3340. // `.hasDataset` will throw an error if the dataset does not exist.
  3341. // no-op in that case
  3342. }
  3343. // Fetch examples first, then create the dataset
  3344. const examples = await sourceClient.listSharedExamples(tokenUuid);
  3345. const dataset = await this.createDataset(finalDatasetName, {
  3346. description: ds.description,
  3347. dataType: ds.data_type || "kv",
  3348. inputsSchema: ds.inputs_schema_definition ?? undefined,
  3349. outputsSchema: ds.outputs_schema_definition ?? undefined,
  3350. });
  3351. try {
  3352. await this.createExamples({
  3353. inputs: examples.map((e) => e.inputs),
  3354. outputs: examples.flatMap((e) => (e.outputs ? [e.outputs] : [])),
  3355. datasetId: dataset.id,
  3356. });
  3357. }
  3358. catch (e) {
  3359. console.error(`An error occurred while creating dataset ${finalDatasetName}. ` +
  3360. "You should delete it manually.");
  3361. throw e;
  3362. }
  3363. }
  3364. parseTokenOrUrl(urlOrToken, apiUrl, numParts = 2, kind = "dataset") {
  3365. // Try parsing as UUID
  3366. try {
  3367. (0, _uuid_js_1.assertUuid)(urlOrToken); // Will throw if it's not a UUID.
  3368. return [apiUrl, urlOrToken];
  3369. }
  3370. catch (_) {
  3371. // no-op if it's not a uuid
  3372. }
  3373. // Parse as URL
  3374. try {
  3375. const parsedUrl = new URL(urlOrToken);
  3376. const pathParts = parsedUrl.pathname
  3377. .split("/")
  3378. .filter((part) => part !== "");
  3379. if (pathParts.length >= numParts) {
  3380. const tokenUuid = pathParts[pathParts.length - numParts];
  3381. return [apiUrl, tokenUuid];
  3382. }
  3383. else {
  3384. throw new Error(`Invalid public ${kind} URL: ${urlOrToken}`);
  3385. }
  3386. }
  3387. catch (error) {
  3388. throw new Error(`Invalid public ${kind} URL or token: ${urlOrToken}`);
  3389. }
  3390. }
  3391. /**
  3392. * Awaits all pending trace batches. Useful for environments where
  3393. * you need to be sure that all tracing requests finish before execution ends,
  3394. * such as serverless environments.
  3395. *
  3396. * @example
  3397. * ```
  3398. * import { Client } from "langsmith";
  3399. *
  3400. * const client = new Client();
  3401. *
  3402. * try {
  3403. * // Tracing happens here
  3404. * ...
  3405. * } finally {
  3406. * await client.awaitPendingTraceBatches();
  3407. * }
  3408. * ```
  3409. *
  3410. * @returns A promise that resolves once all currently pending traces have sent.
  3411. */
  3412. awaitPendingTraceBatches() {
  3413. if (this.manualFlushMode) {
  3414. console.warn("[WARNING]: When tracing in manual flush mode, you must call `await client.flush()` manually to submit trace batches.");
  3415. return Promise.resolve();
  3416. }
  3417. return Promise.all([
  3418. ...this.autoBatchQueue.items.map(({ itemPromise }) => itemPromise),
  3419. this.batchIngestCaller.queue.onIdle(),
  3420. ]);
  3421. }
  3422. }
  3423. exports.Client = Client;
  3424. function isExampleCreate(input) {
  3425. return "dataset_id" in input || "dataset_name" in input;
  3426. }