teckel-ai 0.3.6 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import {z}from'zod';var y=z.object({documentRef:z.string().min(1,"documentRef is required"),documentName:z.string().min(1,"documentName is required"),documentText:z.string().min(1,"documentText is required"),documentLastUpdated:z.string().optional(),sourceUri:z.string().optional(),sourceType:z.string().optional(),similarity:z.number().min(0).max(1).optional(),rank:z.number().int().nonnegative().optional(),ownerEmail:z.string().email().optional(),documentType:z.string().optional()}),b=z.object({prompt:z.number().int().nonnegative(),completion:z.number().int().nonnegative(),total:z.number().int().nonnegative()}),l=z.object({query:z.string().min(1,"query is required").max(1e4,"query too long (max 10,000 characters)"),response:z.string().min(1,"response is required").max(5e4,"response too long (max 50,000 characters)"),model:z.string().optional(),responseTimeMs:z.number().nonnegative().optional(),documents:z.array(y).max(50,"Too many documents (max 50)").optional(),tokens:b.optional(),metadata:z.record(z.string(),z.unknown()).optional(),traceRef:z.string().min(1).optional(),userRef:z.string().min(1).optional()}),h=z.object({sessionRef:z.string().min(1).optional(),userRef:z.string().optional(),metadata:z.record(z.string(),z.unknown()).optional()}),p=z.object({type:z.enum(["thumbs_up","thumbs_down","flag","rating"]),value:z.string().optional(),comment:z.string().optional(),traceRef:z.string().optional()}),f=z.object({apiKey:z.string().min(1,"apiKey is required"),endpoint:z.string().url().optional(),debug:z.boolean().optional(),timeoutMs:z.number().int().positive().max(6e4).optional()});var u=class{constructor(n,e,s,o=false,a={timeoutMs:5e3}){this.turnCount=0;this.sendQueue=Promise.resolve();this.apiKey=n,this.endpoint=e,this.sessionRef=s.sessionRef,this.userRef=s.userRef,this.metadata=s.metadata,this.startedAt=new Date,this.debug=o,this.timeoutMs=a.timeoutMs,this.debug&&console.log("[Teckel] Conversation started:",{sessionRef:this.sessionRef,userRef:this.userRef}),this.startPromise=this._startConversation().catch(i=>{this.debug&&console.warn("[Teckel] Start failed:",i.message);});}trace(n){try{let e=l.parse(n),s=++this.turnCount,o=e.traceRef&&e.traceRef.length>0?e.traceRef:`${this.sessionRef}:${s}`;this.debug&&console.log("[Teckel] Queueing trace:",{sessionRef:this.sessionRef,turnNumber:s,queryLength:e.query.length,responseLength:e.response.length,documentCount:e.documents?.length||0});let a={...e,traceRef:o,userRef:e.userRef||this.userRef};return this.enqueueSend(async()=>{try{await this.startPromise,await this._sendTrace(a);}catch(i){if(this.debug){let d=i instanceof Error?i.message:String(i);console.warn("[Teckel] Trace send failed (non-blocking):",d);}}}),{traceRef:o,turnNumber:s}}catch(e){this.debug&&console.warn("[Teckel] Invalid trace data:",e);}}async feedback(n){try{let e=p.parse(n);this.debug&&console.log("[Teckel] Sending feedback:",{sessionRef:this.sessionRef,type:e.type}),this.enqueueSend(async()=>{try{await this._sendFeedback(e);}catch(s){if(this.debug){let o=s instanceof Error?s.message:String(s);console.warn("[Teckel] Feedback failed:",o);}}});}catch(e){this.debug&&console.warn("[Teckel] Invalid feedback data:",e);}}async end(){let n=Date.now()-this.startedAt.getTime();this.debug&&console.log("[Teckel] Ending conversation:",{sessionRef:this.sessionRef,durationMs:n,turnCount:this.turnCount}),this.enqueueSend(async()=>{try{await this._endConversation(n);}catch(e){if(this.debug){let s=e instanceof Error?e.message:String(e);console.warn("[Teckel] End failed:",s);}}}),await this.flush();}get id(){return this.sessionRef}get turns(){return this.turnCount}get started(){return this.startedAt}async fetchWithRetry(n,e,s){let o=s?.retries??1,a=s?.retryDelayMs??250,i=0,d=r=>new Promise(T=>setTimeout(T,r));for(;;)try{let r=await fetch(n,e);if(!r.ok&&(r.status===429||r.status>=500&&r.status<=599)&&i<o){i++,this.debug&&console.warn("[Teckel] HTTP retry",{url:n,status:r.status,attempt:i}),await d(a+Math.floor(Math.random()*100));continue}return r}catch(r){if(i<o){i++,this.debug&&console.warn("[Teckel] Network retry",{url:n,attempt:i,error:r instanceof Error?r.message:String(r)}),await d(a+Math.floor(Math.random()*100));continue}throw r}}async _startConversation(){let n=await this.fetchWithRetry(`${this.endpoint}/conversations`,{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},keepalive:true,signal:m(this.timeoutMs),body:JSON.stringify({sessionRef:this.sessionRef,userRef:this.userRef,metadata:this.metadata})},{retries:1,retryDelayMs:300});if(!n.ok)throw new Error(`HTTP ${n.status}: ${n.statusText}`)}async _sendTrace(n,e){let s=await this.fetchWithRetry(`${this.endpoint}/conversations/${this.sessionRef}/traces`,{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},keepalive:true,signal:m(e?.timeoutMs??this.timeoutMs),body:JSON.stringify(n)},{retries:1,retryDelayMs:300});if(!s.ok)throw new Error(`HTTP ${s.status}: ${s.statusText}`);return await s.json()}async _sendFeedback(n){let e=await this.fetchWithRetry(`${this.endpoint}/conversations/${this.sessionRef}/feedback`,{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},keepalive:true,signal:m(this.timeoutMs),body:JSON.stringify(n)},{retries:1,retryDelayMs:300});if(!e.ok)throw new Error(`HTTP ${e.status}: ${e.statusText}`)}async _endConversation(n){let e=await this.fetchWithRetry(`${this.endpoint}/conversations/${this.sessionRef}`,{method:"PATCH",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},keepalive:true,signal:m(this.timeoutMs),body:JSON.stringify({durationMs:n,turnCount:this.turnCount})},{retries:1,retryDelayMs:300});if(!e.ok)throw new Error(`HTTP ${e.status}: ${e.statusText}`)}enqueueSend(n){this.sendQueue=this.sendQueue.then(()=>n()).catch(()=>{});}async flush(n){let e=typeof n=="number"&&Number.isFinite(n)&&n>=0?n:this.timeoutMs,s=this.sendQueue.catch(()=>{}),o;try{await Promise.race([s,new Promise((a,i)=>{o=setTimeout(()=>i(new Error("Flush timeout")),e);})]);}catch(a){if(this.debug){let i=a instanceof Error?a.message:String(a);console.warn("[Teckel] Flush incomplete:",i);}throw a}finally{o&&clearTimeout(o);}}};function m(c){if(!(typeof c!="number"||c<=0)&&typeof AbortSignal.timeout=="function")return AbortSignal.timeout(c)}var g=class{constructor(n){let e=f.parse(n);e.apiKey.startsWith("tk_live_")||console.warn('[Teckel] API key should start with "tk_live_". Current key: '+e.apiKey.substring(0,10)+"..."),this.apiKey=e.apiKey,this.endpoint=e.endpoint||"https://app.teckel.ai/api",this.debug=e.debug||false,this.timeoutMs=typeof e.timeoutMs=="number"?e.timeoutMs:5e3,this.debug&&console.log("[Teckel] SDK initialized:",{endpoint:this.endpoint,version:"0.3.6",timeoutMs:this.timeoutMs});}start(n={}){let e=h.parse(n),s=e.sessionRef||`auto:${crypto.randomUUID().slice(0,8)}`;return new u(this.apiKey,this.endpoint,{...e,sessionRef:s},this.debug,{timeoutMs:this.timeoutMs})}};
2
- export{u as Conversation,h as ConversationOptionsSchema,y as DocumentSchema,p as FeedbackDataSchema,f as TeckelConfigSchema,g as TeckelTracer,b as TokenUsageSchema,l as TraceDataSchema};
1
+ import {z,ZodError}from'zod';var c={METADATA_MAX_BYTES:1e4,TRACE_MAX_BYTES:3e6,MAX_DOCUMENTS:15},u=z.object({id:z.string().min(1,"id is required").max(500),name:z.string().min(1,"name is required").max(500),text:z.string().min(1,"text is required").max(5e4),lastUpdated:z.string().datetime({offset:true}).optional(),url:z.string().max(2e3).optional(),source:z.string().max(100).optional(),fileFormat:z.string().max(100).optional(),similarity:z.number().min(0).max(1).optional(),rank:z.number().int().nonnegative().optional(),ownerEmail:z.string().email().max(254).optional()}),g=z.object({prompt:z.number().int().nonnegative(),completion:z.number().int().nonnegative(),total:z.number().int().nonnegative()}),m=z.object({query:z.string().min(1,"query is required").max(1e4,"query too long (max 10,000 chars)"),response:z.string().min(1,"response is required").max(5e4,"response too long (max 50,000 chars)"),model:z.string().max(100).optional(),latencyMs:z.number().int().nonnegative().max(6e5).optional(),tokens:g.optional(),documents:z.array(u).max(c.MAX_DOCUMENTS,`Too many documents (max ${c.MAX_DOCUMENTS})`).optional(),sessionId:z.string().min(1).max(200,"sessionId must be 200 chars or less").optional(),userId:z.string().min(1).max(255).optional(),traceId:z.string().uuid("traceId must be a valid UUID").optional(),systemPrompt:z.string().max(5e4,"systemPrompt too long (max 50,000 chars)").optional(),metadata:z.record(z.string(),z.unknown()).optional().refine(r=>!r||JSON.stringify(r).length<=c.METADATA_MAX_BYTES,`metadata exceeds ${c.METADATA_MAX_BYTES/1e3}KB limit`)}).superRefine((r,i)=>{JSON.stringify(r).length>c.TRACE_MAX_BYTES&&i.addIssue({code:z.ZodIssueCode.custom,message:`trace payload exceeds ${c.TRACE_MAX_BYTES/1e6}MB limit`,path:[]});}),d=z.object({traceId:z.string().uuid("traceId must be a valid UUID").optional(),sessionId:z.string().min(1).max(200,"sessionId must be 200 chars or less").optional(),type:z.enum(["thumbs_up","thumbs_down","flag","rating"]),value:z.string().min(1).max(10).optional(),comment:z.string().max(2e3).optional()}).refine(r=>r.traceId||r.sessionId,{message:"Either traceId or sessionId is required"}),l=z.object({apiKey:z.string().min(1,"apiKey is required"),endpoint:z.string().url().optional(),debug:z.boolean().optional(),timeoutMs:z.number().int().positive().max(6e4).optional()});var p=class{constructor(i){this.sendQueue=Promise.resolve();let e=l.parse(i);if(!e.apiKey.startsWith("tk_live_")){let o=e.apiKey.startsWith("tk_")?"tk_***":"invalid prefix";console.error(`[Teckel] Invalid API key format. Expected "tk_live_***", got "${o}". Get your key at https://app.teckel.ai/settings/api-keys`);}this.apiKey=e.apiKey,this.endpoint=e.endpoint||"https://app.teckel.ai/api",this.debug=e.debug||false,this.timeoutMs=e.timeoutMs??5e3,this.debug&&console.log("[Teckel] Initialized",{endpoint:this.endpoint,timeoutMs:this.timeoutMs});}trace(i){try{let e=m.parse(i);this.debug&&console.log("[Teckel] Queueing trace:",{traceId:e.traceId,sessionId:e.sessionId,queryLength:e.query.length,documentCount:e.documents?.length||0}),this.enqueue(()=>this.send("/sdk/traces",e),"trace");}catch(e){this.logValidationError("Trace",e);}}feedback(i){try{let e=d.parse(i);this.debug&&console.log("[Teckel] Sending feedback:",{type:e.type,traceId:e.traceId}),this.enqueue(()=>this.send("/sdk/feedback",e),"feedback");}catch(e){this.logValidationError("Feedback",e);}}async flush(i){let e=i??this.timeoutMs,o=this.sendQueue.catch(()=>{}),n;try{await Promise.race([o,new Promise((a,s)=>{n=setTimeout(()=>s(new Error("Flush timeout")),e);})]);}catch(a){throw this.debug&&console.warn("[Teckel] Flush incomplete:",a.message),a}finally{n&&clearTimeout(n);}}async send(i,e){let o=`${this.endpoint}${i}`,n=await this.fetchWithRetry(o,{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},keepalive:true,signal:AbortSignal.timeout?.(this.timeoutMs),body:JSON.stringify(e)});if(!n.ok)throw this.logHttpError(i,n.status),new Error(`HTTP ${n.status}`);return n.json()}async fetchWithRetry(i,e){let n=()=>new Promise(a=>setTimeout(a,250+Math.random()*100));for(let a=1;a<=2;a++)try{let s=await fetch(i,e);if(s.ok||a===2)return s;if(s.status===429||s.status>=500){this.debug&&console.warn("[Teckel] Retrying",{status:s.status,attempt:a}),await n();continue}return s}catch(s){if(a===2)throw s;this.debug&&console.warn("[Teckel] Retry after error",{attempt:a}),await n();}throw new Error("Unreachable")}enqueue(i,e){this.sendQueue=this.sendQueue.then(()=>i()).catch(o=>{if(!(o instanceof Error&&o.message.startsWith("HTTP "))){let n=o instanceof Error?o.message:String(o);n.includes("timeout")||n.includes("abort")?console.warn(`[Teckel] ${e} timed out. Consider increasing timeoutMs.`):console.warn(`[Teckel] ${e} failed: ${n}`);}});}logValidationError(i,e){if(e instanceof ZodError){let o=e.errors.map(n=>n.path.length?`${n.path.join(".")}: ${n.message}`:n.message).join("; ");console.error(`[Teckel] ${i} dropped - validation failed: ${o}`);}else this.debug&&console.error(`[Teckel] ${i} dropped:`,e);}logHttpError(i,e){let o=i.includes("trace")?"Trace":"Feedback",a={401:`${o} failed - invalid API key. Check https://app.teckel.ai/settings/api-keys`,400:`${o} failed - invalid request. Enable debug mode for details.`,404:`${o} failed - endpoint not found. Check your configuration.`,429:`${o} dropped - rate limit exceeded.`}[e]||(e>=500?`${o} dropped - server error (${e})`:`${o} failed - HTTP ${e}`);e>=500||e===429?console.warn(`[Teckel] ${a}`):console.error(`[Teckel] ${a}`);}};
2
+ export{u as DocumentSchema,d as FeedbackDataSchema,c as TRACE_SIZE_LIMITS,l as TeckelConfigSchema,p as TeckelTracer,g as TokenUsageSchema,m as TraceDataSchema};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "teckel-ai",
3
- "version": "0.3.6",
4
- "description": "Simple SDK for AI conversation tracking and RAG observability",
3
+ "version": "0.5.1",
4
+ "description": "Simple SDK for AI trace tracking and RAG observability",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
7
7
  "types": "dist/index.d.ts",
@@ -25,7 +25,10 @@
25
25
  "build": "tsup",
26
26
  "clean": "rm -rf dist",
27
27
  "prepublishOnly": "npm run clean && npm run build",
28
- "type-check": "tsc --noEmit"
28
+ "type-check": "tsc --noEmit",
29
+ "test": "vitest run --exclude tests/integration.test.ts",
30
+ "test:watch": "vitest --exclude tests/integration.test.ts",
31
+ "test:integration": "vitest run tests/integration.test.ts"
29
32
  },
30
33
  "keywords": [
31
34
  "llm",
@@ -47,8 +50,11 @@
47
50
  "zod": "^3.23.8"
48
51
  },
49
52
  "devDependencies": {
50
- "tsup": "^8.3.5",
51
- "typescript": "^5.0.0"
53
+ "cac": "^6.7.14",
54
+ "joycon": "^3.1.1",
55
+ "tsup": "^8.5.1",
56
+ "typescript": "^5.0.0",
57
+ "vitest": "^2.1.0"
52
58
  },
53
59
  "homepage": "https://teckel.ai",
54
60
  "bugs": {