tekimax-ts 0.2.0 → 0.2.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/README.md CHANGED
@@ -18,7 +18,7 @@ npm install tekimax-ts
18
18
  ## 🌟 Features
19
19
 
20
20
  - **Universal API**: One interface for all providers. Switch from OpenAI to Ollama with a single config change.
21
- - **Type Safety**: End-to-end TypeScript support. Zod schemas for runtime validation.
21
+ - **Strict Modality Type Safety**: End-to-end TypeScript support. Strong capability interfaces ensure compile-time safety (e.g., your code won't compile if you call `.images.generate()` on a provider missing the `ImageGenerationCapability`). Zod schemas for runtime validation.
22
22
  - **Zero Latency**: Lightweight adapter pattern with zero runtime overhead.
23
23
  - **Zero CVEs**: Hardened supply chain using Chainguard images.
24
24
  - **Redis Adapter** _(optional)_: Response caching, rate limiting, token budgets, and session storage with any Redis client.
@@ -53,6 +53,60 @@ const claude = new Tekimax({
53
53
  const local = new Tekimax({
54
54
  provider: new OllamaProvider({ baseUrl: 'http://localhost:11434' })
55
55
  })
56
+
57
+ // Custom Model Proxies (e.g. Internal API gateways)
58
+ const proxyClient = new Tekimax({
59
+ provider: new OpenAIProvider({
60
+ apiKey: process.env.CUSTOM_PROXY_KEY,
61
+ baseURL: 'https://api.my-custom-proxy.internal/v1'
62
+ })
63
+ })
64
+
65
+ ### Streaming
66
+
67
+ Tekimax-TS uses standard Javascript Async Iterables for real-time streaming:
68
+
69
+ ```typescript
70
+ const stream = await client.text.generateStream({
71
+ model: "gpt-4o",
72
+ messages: [{ role: "user", content: "Tell me a story" }]
73
+ })
74
+
75
+ for await (const chunk of stream) {
76
+ process.stdout.write(chunk.delta)
77
+ }
78
+ ```
79
+
80
+ ## 🔌 Plugin Architecture (Lifecycle Hooks)
81
+
82
+ The SDK provides a robust middleware architecture via `plugins`. You can snap in pre-built plugins for Security, Scalability, and Telemetry, or build your own.
83
+
84
+ ```typescript
85
+ import { Tekimax, OpenAIProvider, PIIFilterPlugin, LoggerPlugin, MaxContextOverflowPlugin } from 'tekimax-ts'
86
+
87
+ const client = new Tekimax({
88
+ provider: new OpenAIProvider({ apiKey: 'sk-...' }),
89
+ plugins: [
90
+ new LoggerPlugin(), // Telemetry
91
+ new PIIFilterPlugin(), // Security: Redacts emails and SSNs
92
+ new MaxContextOverflowPlugin(15) // Scalability: Prevents context bloat in long loops
93
+ ]
94
+ })
95
+ ```
96
+
97
+ ### Building Custom Plugins
98
+ Implement the `TekimaxPlugin` interface to hook into the request/response lifecycle:
99
+
100
+ ```typescript
101
+ export interface TekimaxPlugin {
102
+ name: string
103
+ onInit?: (client: Tekimax) => void
104
+ beforeRequest?: (context: PluginContext) => Promise<void | PluginContext>
105
+ afterResponse?: (context: PluginContext, result: ChatResult) => Promise<void>
106
+ onStreamChunk?: (context: PluginContext, chunk: StreamChunk) => void
107
+ beforeToolExecute?: (toolName: string, args: unknown) => Promise<void>
108
+ afterToolExecute?: (toolName: string, result: unknown) => Promise<void>
109
+ }
56
110
  ```
57
111
 
58
112
  ### 2. Multi-Modal Interfaces
package/dist/index.cjs CHANGED
@@ -36,9 +36,12 @@ __export(index_exports, {
36
36
  FallbackProvider: () => FallbackProvider,
37
37
  GeminiProvider: () => GeminiProvider,
38
38
  GrokProvider: () => GrokProvider,
39
+ LoggerPlugin: () => LoggerPlugin,
40
+ MaxContextOverflowPlugin: () => MaxContextOverflowPlugin,
39
41
  OllamaProvider: () => OllamaProvider,
40
42
  OpenAIProvider: () => OpenAIProvider,
41
43
  OpenRouterProvider: () => OpenRouterProvider,
44
+ PIIFilterPlugin: () => PIIFilterPlugin,
42
45
  RateLimiter: () => RateLimiter,
43
46
  ResponseCache: () => ResponseCache,
44
47
  SessionStore: () => SessionStore,
@@ -1084,7 +1087,7 @@ var OpenAIProvider = class {
1084
1087
  this.name = "openai";
1085
1088
  this.client = new import_openai.default({
1086
1089
  apiKey: options.apiKey,
1087
- dangerouslyAllowBrowser: options.dangerouslyAllowBrowser
1090
+ baseURL: options.baseURL
1088
1091
  });
1089
1092
  }
1090
1093
  async generateSpeech(options) {
@@ -1985,22 +1988,41 @@ function tool(def) {
1985
1988
  // src/core/generate.ts
1986
1989
  async function generateText({
1987
1990
  adapter,
1991
+ plugins = [],
1988
1992
  ...options
1989
1993
  }) {
1990
1994
  const { model, tools, maxSteps = 1, temperature, maxTokens, signal } = options;
1991
- const currentMessages = [...options.messages];
1995
+ let currentMessages = [...options.messages];
1996
+ let currentModel = model;
1992
1997
  let steps = 0;
1993
1998
  const toolDefinitions = tools ? Object.values(tools) : void 0;
1994
1999
  while (steps < maxSteps) {
1995
2000
  steps++;
2001
+ let context = {
2002
+ model: currentModel,
2003
+ messages: [...currentMessages],
2004
+ timestamp: Date.now(),
2005
+ requestOptions: { ...options }
2006
+ };
2007
+ for (const plugin of plugins) {
2008
+ if (plugin.beforeRequest) {
2009
+ const updated = await plugin.beforeRequest(context);
2010
+ if (updated) context = updated;
2011
+ }
2012
+ }
2013
+ currentMessages = context.messages;
2014
+ currentModel = context.model;
1996
2015
  const result = await adapter.chat({
1997
- model,
2016
+ model: currentModel,
1998
2017
  messages: currentMessages,
1999
2018
  tools: toolDefinitions,
2000
2019
  temperature,
2001
2020
  maxTokens,
2002
2021
  signal
2003
2022
  });
2023
+ for (const plugin of plugins) {
2024
+ if (plugin.afterResponse) await plugin.afterResponse(context, result);
2025
+ }
2004
2026
  const { message } = result;
2005
2027
  currentMessages.push(message);
2006
2028
  if (!message.toolCalls || message.toolCalls.length === 0) {
@@ -2014,25 +2036,23 @@ async function generateText({
2014
2036
  }
2015
2037
  const toolResults = await Promise.all(
2016
2038
  message.toolCalls.map(async (call) => {
2017
- const tool2 = tools?.[call.function.name];
2039
+ const toolName = call.function.name;
2040
+ const tool2 = tools?.[toolName];
2018
2041
  if (!tool2) {
2019
- return {
2020
- id: call.id,
2021
- result: `Error: Tool ${call.function.name} not found`
2022
- };
2042
+ return { id: call.id, result: `Error: Tool ${toolName} not found` };
2023
2043
  }
2024
2044
  try {
2025
- const args = JSON.parse(call.function.arguments);
2045
+ let args = JSON.parse(call.function.arguments);
2046
+ for (const plugin of plugins) {
2047
+ if (plugin.beforeToolExecute) await plugin.beforeToolExecute(toolName, args);
2048
+ }
2026
2049
  const output = await tool2.execute(args);
2027
- return {
2028
- id: call.id,
2029
- result: output
2030
- };
2050
+ for (const plugin of plugins) {
2051
+ if (plugin.afterToolExecute) await plugin.afterToolExecute(toolName, output);
2052
+ }
2053
+ return { id: call.id, result: output };
2031
2054
  } catch (error) {
2032
- return {
2033
- id: call.id,
2034
- result: `Error executing tool: ${error.message}`
2035
- };
2055
+ return { id: call.id, result: `Error executing tool: ${error.message}` };
2036
2056
  }
2037
2057
  })
2038
2058
  );
@@ -2050,7 +2070,6 @@ async function generateText({
2050
2070
  toolCalls: [],
2051
2071
  toolResults: [],
2052
2072
  finishReason: "length",
2053
- // or 'tool_calls' if strictly ending on tools
2054
2073
  usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
2055
2074
  warnings: ["Max steps reached"]
2056
2075
  };
@@ -2306,69 +2325,80 @@ async function withRetry(fn, options = {}) {
2306
2325
  throw lastError || new Error("withRetry: unexpected exit");
2307
2326
  }
2308
2327
  function createRetryProvider(provider, options = {}) {
2309
- return {
2310
- ...provider,
2311
- name: provider.name,
2312
- chat: (chatOptions) => {
2313
- return withRetry(
2314
- () => provider.chat(chatOptions),
2315
- { ...options, signal: chatOptions.signal || options.signal }
2316
- );
2317
- },
2318
- chatStream: (chatOptions) => {
2319
- const iterable = {
2320
- [Symbol.asyncIterator]() {
2321
- let innerIterator = null;
2322
- return {
2323
- async next() {
2324
- if (!innerIterator) {
2325
- const stream = await withRetry(
2326
- async () => provider.chatStream(chatOptions),
2327
- { ...options, signal: chatOptions.signal || options.signal }
2328
- );
2329
- innerIterator = stream[Symbol.asyncIterator]();
2330
- }
2331
- return innerIterator.next();
2332
- },
2333
- async return() {
2334
- if (innerIterator?.return) {
2335
- return innerIterator.return();
2328
+ return new Proxy(provider, {
2329
+ get(target, prop, receiver) {
2330
+ const value = Reflect.get(target, prop, receiver);
2331
+ if (typeof value === "function" && typeof prop === "string") {
2332
+ if (prop === "chatStream") {
2333
+ return function(chatOptions) {
2334
+ const iterable = {
2335
+ [Symbol.asyncIterator]() {
2336
+ let innerIterator = null;
2337
+ return {
2338
+ async next() {
2339
+ if (!innerIterator) {
2340
+ const stream = await withRetry(
2341
+ async () => value.call(target, chatOptions),
2342
+ { ...options, signal: chatOptions.signal || options.signal }
2343
+ );
2344
+ innerIterator = stream[Symbol.asyncIterator]();
2345
+ }
2346
+ return innerIterator.next();
2347
+ },
2348
+ async return() {
2349
+ if (innerIterator?.return) {
2350
+ return innerIterator.return();
2351
+ }
2352
+ return { value: void 0, done: true };
2353
+ }
2354
+ };
2336
2355
  }
2337
- return { value: void 0, done: true };
2338
- }
2356
+ };
2357
+ return iterable;
2339
2358
  };
2340
2359
  }
2341
- };
2342
- return iterable;
2343
- },
2344
- // Pass through multi-modal methods with retry
2345
- generateImage: provider.generateImage ? (opts) => withRetry(() => provider.generateImage(opts), options) : void 0,
2346
- editImage: provider.editImage ? (opts) => withRetry(() => provider.editImage(opts), options) : void 0,
2347
- analyzeImage: provider.analyzeImage ? (opts) => withRetry(() => provider.analyzeImage(opts), options) : void 0,
2348
- generateSpeech: provider.generateSpeech ? (opts) => withRetry(() => provider.generateSpeech(opts), options) : void 0,
2349
- transcribeAudio: provider.transcribeAudio ? (opts) => withRetry(() => provider.transcribeAudio(opts), options) : void 0,
2350
- generateVideo: provider.generateVideo ? (opts) => withRetry(() => provider.generateVideo(opts), options) : void 0,
2351
- analyzeVideo: provider.analyzeVideo ? (opts) => withRetry(() => provider.analyzeVideo(opts), options) : void 0
2352
- };
2360
+ const promiseMethods = [
2361
+ "chat",
2362
+ "generateImage",
2363
+ "editImage",
2364
+ "analyzeImage",
2365
+ "generateSpeech",
2366
+ "transcribeAudio",
2367
+ "generateVideo",
2368
+ "analyzeVideo",
2369
+ "embed"
2370
+ ];
2371
+ if (promiseMethods.includes(prop)) {
2372
+ return function(...args) {
2373
+ const callOpts = args[0] || {};
2374
+ const sig = callOpts.signal || options.signal;
2375
+ return withRetry(() => value.apply(target, args), { ...options, signal: sig });
2376
+ };
2377
+ }
2378
+ }
2379
+ if (typeof value === "function") {
2380
+ return value.bind(target);
2381
+ }
2382
+ return value;
2383
+ }
2384
+ });
2353
2385
  }
2354
2386
 
2355
2387
  // src/core/middleware.ts
2356
2388
  function wrapProvider(provider, middlewares) {
2357
- return {
2358
- ...provider,
2359
- name: provider.name,
2360
- chat: createWrappedChat(provider, middlewares),
2361
- chatStream: createWrappedChatStream(provider, middlewares),
2362
- // Multi-modal methods pass through unmodified.
2363
- // Middleware only applies to chat/chatStream for now.
2364
- generateImage: provider.generateImage?.bind(provider),
2365
- editImage: provider.editImage?.bind(provider),
2366
- analyzeImage: provider.analyzeImage?.bind(provider),
2367
- generateSpeech: provider.generateSpeech?.bind(provider),
2368
- transcribeAudio: provider.transcribeAudio?.bind(provider),
2369
- generateVideo: provider.generateVideo?.bind(provider),
2370
- analyzeVideo: provider.analyzeVideo?.bind(provider)
2371
- };
2389
+ const wrappedChat = createWrappedChat(provider, middlewares);
2390
+ const wrappedChatStream = createWrappedChatStream(provider, middlewares);
2391
+ return new Proxy(provider, {
2392
+ get(target, prop, receiver) {
2393
+ if (prop === "chat") return wrappedChat;
2394
+ if (prop === "chatStream") return wrappedChatStream;
2395
+ const value = Reflect.get(target, prop, receiver);
2396
+ if (typeof value === "function") {
2397
+ return value.bind(target);
2398
+ }
2399
+ return value;
2400
+ }
2401
+ });
2372
2402
  }
2373
2403
  function createWrappedChat(provider, middlewares) {
2374
2404
  return async (options) => {
@@ -2517,35 +2547,35 @@ var FallbackProvider = class {
2517
2547
  }
2518
2548
  // --- Multi-modal: delegate to first provider that supports the capability ---
2519
2549
  get generateImage() {
2520
- const provider = this.providers.find((p) => p.generateImage);
2550
+ const provider = this.providers.find((p) => typeof p.generateImage === "function");
2521
2551
  return provider?.generateImage?.bind(provider);
2522
2552
  }
2523
2553
  get editImage() {
2524
- const provider = this.providers.find((p) => p.editImage);
2554
+ const provider = this.providers.find((p) => typeof p.editImage === "function");
2525
2555
  return provider?.editImage?.bind(provider);
2526
2556
  }
2527
2557
  get analyzeImage() {
2528
- const provider = this.providers.find((p) => p.analyzeImage);
2558
+ const provider = this.providers.find((p) => typeof p.analyzeImage === "function");
2529
2559
  return provider?.analyzeImage?.bind(provider);
2530
2560
  }
2531
2561
  get generateSpeech() {
2532
- const provider = this.providers.find((p) => p.generateSpeech);
2562
+ const provider = this.providers.find((p) => typeof p.generateSpeech === "function");
2533
2563
  return provider?.generateSpeech?.bind(provider);
2534
2564
  }
2535
2565
  get transcribeAudio() {
2536
- const provider = this.providers.find((p) => p.transcribeAudio);
2566
+ const provider = this.providers.find((p) => typeof p.transcribeAudio === "function");
2537
2567
  return provider?.transcribeAudio?.bind(provider);
2538
2568
  }
2539
2569
  get generateVideo() {
2540
- const provider = this.providers.find((p) => p.generateVideo);
2570
+ const provider = this.providers.find((p) => typeof p.generateVideo === "function");
2541
2571
  return provider?.generateVideo?.bind(provider);
2542
2572
  }
2543
2573
  get analyzeVideo() {
2544
- const provider = this.providers.find((p) => p.analyzeVideo);
2574
+ const provider = this.providers.find((p) => typeof p.analyzeVideo === "function");
2545
2575
  return provider?.analyzeVideo?.bind(provider);
2546
2576
  }
2547
2577
  get embed() {
2548
- const provider = this.providers.find((p) => p.embed);
2578
+ const provider = this.providers.find((p) => typeof p.embed === "function");
2549
2579
  return provider?.embed?.bind(provider);
2550
2580
  }
2551
2581
  /**
@@ -2573,7 +2603,6 @@ var FallbackProvider = class {
2573
2603
  };
2574
2604
 
2575
2605
  // src/core/convex.ts
2576
- var import_child_process = require("child_process");
2577
2606
  var MANAGEMENT_API = "https://api.convex.dev/v1";
2578
2607
  var ConvexManager = class {
2579
2608
  constructor(options = {}) {
@@ -2656,20 +2685,17 @@ var ConvexManager = class {
2656
2685
  }
2657
2686
  /**
2658
2687
  * Push Convex functions and schema to a deployment.
2659
- * Shells out to `npx convex deploy` with the given deploy key.
2688
+ * Use the Convex CLI directly to run this command.
2660
2689
  */
2661
2690
  deploy(deployKey, options = {}) {
2662
2691
  const cwd = options.projectDir || process.cwd();
2663
2692
  const flags = options.flags?.join(" ") || "";
2664
- const cmd = `npx convex deploy --cmd-url-env-var-name CONVEX_URL ${flags}`.trim();
2665
- (0, import_child_process.execSync)(cmd, {
2666
- cwd,
2667
- stdio: "inherit",
2668
- env: {
2669
- ...process.env,
2670
- CONVEX_DEPLOY_KEY: deployKey
2671
- }
2672
- });
2693
+ const cmd = `CONVEX_DEPLOY_KEY=${deployKey} npx convex deploy --cmd-url-env-var-name CONVEX_URL ${flags}`.trim();
2694
+ throw new Error(
2695
+ `Deploys must be executed via the CLI. Run the following command in your terminal from ${cwd}:
2696
+
2697
+ ${cmd}`
2698
+ );
2673
2699
  }
2674
2700
  // ─── Discovery ──────────────────────────────────────────────────────
2675
2701
  /**
@@ -2691,10 +2717,7 @@ var ConvexManager = class {
2691
2717
  }
2692
2718
  /**
2693
2719
  * Generate an OpenAPI specification from a Convex deployment.
2694
- *
2695
- * Uses `npx convex-helpers open-api-spec` to introspect the deployment
2696
- * and produce a `convex-spec.yaml` file. You can then use tools like
2697
- * openapi-generator-cli to create type-safe clients in Go, Java, etc.
2720
+ * Use the Convex CLI directly to run this command.
2698
2721
  *
2699
2722
  * @see https://docs.convex.dev/client/open-api
2700
2723
  */
@@ -2703,10 +2726,11 @@ var ConvexManager = class {
2703
2726
  const output = options.output || "convex-spec.yaml";
2704
2727
  const extraFlags = options.flags?.join(" ") || "";
2705
2728
  const cmd = `npx convex-helpers open-api-spec --output ${output} ${extraFlags}`.trim();
2706
- (0, import_child_process.execSync)(cmd, {
2707
- cwd,
2708
- stdio: "inherit"
2709
- });
2729
+ throw new Error(
2730
+ `OpenAPI generation must be executed via the CLI. Run the following command in your terminal from ${cwd}:
2731
+
2732
+ ${cmd}`
2733
+ );
2710
2734
  }
2711
2735
  // ─── Internal Helpers ───────────────────────────────────────────────
2712
2736
  async resolveTeamId() {
@@ -2739,7 +2763,6 @@ var ConvexManager = class {
2739
2763
  };
2740
2764
 
2741
2765
  // src/core/cache.ts
2742
- var import_crypto = require("crypto");
2743
2766
  var ResponseCache = class {
2744
2767
  constructor(redis, options = {}) {
2745
2768
  this.redis = redis;
@@ -2762,7 +2785,11 @@ var ResponseCache = class {
2762
2785
  }
2763
2786
  defaultKeyFn(model, messages) {
2764
2787
  const payload = JSON.stringify({ model, messages });
2765
- return (0, import_crypto.createHash)("sha256").update(payload).digest("hex");
2788
+ let hash = 5381;
2789
+ for (let i = 0; i < payload.length; i++) {
2790
+ hash = (hash << 5) + hash + payload.charCodeAt(i);
2791
+ }
2792
+ return (hash >>> 0).toString(16);
2766
2793
  }
2767
2794
  };
2768
2795
  var RateLimiter = class {
@@ -2859,28 +2886,55 @@ var SessionStore = class {
2859
2886
 
2860
2887
  // src/namespaces/text.ts
2861
2888
  var TextNamespace = class {
2862
- constructor(provider) {
2889
+ constructor(provider, plugins = []) {
2863
2890
  this.provider = provider;
2891
+ this.plugins = plugins;
2892
+ }
2893
+ async runBeforeRequest(options) {
2894
+ let context = {
2895
+ model: options.model,
2896
+ messages: [...options.messages],
2897
+ timestamp: Date.now(),
2898
+ requestOptions: { ...options }
2899
+ };
2900
+ for (const plugin of this.plugins) {
2901
+ if (plugin.beforeRequest) {
2902
+ const updated = await plugin.beforeRequest(context);
2903
+ if (updated) context = updated;
2904
+ }
2905
+ }
2906
+ return context;
2864
2907
  }
2865
2908
  /**
2866
2909
  * Generate text from a prompt (Chat Completion).
2867
2910
  */
2868
2911
  async generate(options) {
2869
- return this.provider.chat(options);
2912
+ const context = await this.runBeforeRequest(options);
2913
+ const activeOptions = { ...options, messages: context.messages, model: context.model };
2914
+ const result = await this.provider.chat(activeOptions);
2915
+ for (const plugin of this.plugins) {
2916
+ if (plugin.afterResponse) await plugin.afterResponse(context, result);
2917
+ }
2918
+ return result;
2870
2919
  }
2871
2920
  /**
2872
2921
  * Stream text generation.
2873
2922
  */
2874
2923
  async *generateStream(options) {
2875
- for await (const chunk of this.provider.chatStream(options)) {
2924
+ const context = await this.runBeforeRequest(options);
2925
+ const activeOptions = { ...options, messages: context.messages, model: context.model };
2926
+ for await (const chunk of this.provider.chatStream(activeOptions)) {
2927
+ for (const plugin of this.plugins) {
2928
+ if (plugin.onStreamChunk) plugin.onStreamChunk(context, chunk);
2929
+ }
2876
2930
  yield chunk;
2877
2931
  }
2878
2932
  }
2879
2933
  /**
2880
- * Generate embeddings for text input(s).
2934
+ * Generate embeddings for text input(s). Only available if the provider supports embeddings.
2881
2935
  */
2882
2936
  async embed(options) {
2883
- if (!this.provider.embed) {
2937
+ if (!("embed" in this.provider)) {
2884
2938
  throw new Error(`Provider '${this.provider.name}' does not support embeddings`);
2885
2939
  }
2886
2940
  return this.provider.embed(options);
@@ -2902,28 +2956,28 @@ var ImagesNamespace = class {
2902
2956
  this.provider = provider;
2903
2957
  }
2904
2958
  /**
2905
- * Generate images from a prompt.
2959
+ * Generate images from a prompt. Only available if the provider supports image generation.
2906
2960
  */
2907
2961
  async generate(options) {
2908
- if (!this.provider.generateImage) {
2962
+ if (!("generateImage" in this.provider)) {
2909
2963
  throw new Error(`Provider '${this.provider.name}' does not support image generation`);
2910
2964
  }
2911
2965
  return this.provider.generateImage(options);
2912
2966
  }
2913
2967
  /**
2914
- * Edit an image with a prompt.
2968
+ * Edit an image with a prompt. Only available if the provider supports image editing.
2915
2969
  */
2916
2970
  async edit(options) {
2917
- if (!this.provider.editImage) {
2971
+ if (!("editImage" in this.provider)) {
2918
2972
  throw new Error(`Provider '${this.provider.name}' does not support image editing`);
2919
2973
  }
2920
2974
  return this.provider.editImage(options);
2921
2975
  }
2922
2976
  /**
2923
- * Analyze an image (Vision).
2977
+ * Analyze an image (Vision). Only available if the provider supports vision analytics.
2924
2978
  */
2925
2979
  async analyze(options) {
2926
- if (!this.provider.analyzeImage) {
2980
+ if (!("analyzeImage" in this.provider)) {
2927
2981
  throw new Error(`Provider '${this.provider.name}' does not support image analysis`);
2928
2982
  }
2929
2983
  return this.provider.analyzeImage(options);
@@ -2936,19 +2990,19 @@ var AudioNamespace = class {
2936
2990
  this.provider = provider;
2937
2991
  }
2938
2992
  /**
2939
- * Convert text to speech.
2993
+ * Convert text to speech. Only available if the provider supports TTS.
2940
2994
  */
2941
2995
  async speak(options) {
2942
- if (!this.provider.generateSpeech) {
2996
+ if (!("generateSpeech" in this.provider)) {
2943
2997
  throw new Error(`Provider '${this.provider.name}' does not support speech generation`);
2944
2998
  }
2945
2999
  return this.provider.generateSpeech(options);
2946
3000
  }
2947
3001
  /**
2948
- * Transcribe audio to text.
3002
+ * Transcribe audio to text. Only available if the provider supports STT.
2949
3003
  */
2950
3004
  async transcribe(options) {
2951
- if (!this.provider.transcribeAudio) {
3005
+ if (!("transcribeAudio" in this.provider)) {
2952
3006
  throw new Error(`Provider '${this.provider.name}' does not support audio transcription`);
2953
3007
  }
2954
3008
  return this.provider.transcribeAudio(options);
@@ -2961,19 +3015,19 @@ var VideosNamespace = class {
2961
3015
  this.provider = provider;
2962
3016
  }
2963
3017
  /**
2964
- * Generate a video from a prompt.
3018
+ * Generate a video from a prompt. Only available if the provider supports video generation.
2965
3019
  */
2966
3020
  async generate(options) {
2967
- if (!this.provider.generateVideo) {
3021
+ if (!("generateVideo" in this.provider)) {
2968
3022
  throw new Error(`Provider '${this.provider.name}' does not support video generation`);
2969
3023
  }
2970
3024
  return this.provider.generateVideo(options);
2971
3025
  }
2972
3026
  /**
2973
- * Analyze a video (Video-to-Text).
3027
+ * Analyze a video (Video-to-Text). Only available if the provider supports video analysis.
2974
3028
  */
2975
3029
  async analyze(options) {
2976
- if (!this.provider.analyzeVideo) {
3030
+ if (!("analyzeVideo" in this.provider)) {
2977
3031
  throw new Error(`Provider '${this.provider.name}' does not support video analysis`);
2978
3032
  }
2979
3033
  return this.provider.analyzeVideo(options);
@@ -2984,7 +3038,11 @@ var VideosNamespace = class {
2984
3038
  var Tekimax = class {
2985
3039
  constructor(options) {
2986
3040
  this.provider = options.provider;
2987
- this.text = new TextNamespace(this.provider);
3041
+ this.plugins = options.plugins || [];
3042
+ for (const plugin of this.plugins) {
3043
+ if (plugin.onInit) plugin.onInit(this);
3044
+ }
3045
+ this.text = new TextNamespace(this.provider, this.plugins);
2988
3046
  this.images = new ImagesNamespace(this.provider);
2989
3047
  this.audio = new AudioNamespace(this.provider);
2990
3048
  this.videos = new VideosNamespace(this.provider);
@@ -2996,6 +3054,70 @@ var Tekimax = class {
2996
3054
  return this.text.chat;
2997
3055
  }
2998
3056
  };
3057
+
3058
+ // src/plugins/index.ts
3059
+ var PIIFilterPlugin = class {
3060
+ constructor() {
3061
+ this.name = "PIIFilterPlugin";
3062
+ this.patterns = {
3063
+ email: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
3064
+ ssn: /\b\d{3}-\d{2}-\d{4}\b/g
3065
+ };
3066
+ }
3067
+ async beforeRequest(context) {
3068
+ context.messages = context.messages.map((msg) => {
3069
+ if (typeof msg.content === "string") {
3070
+ let safeContent = msg.content;
3071
+ safeContent = safeContent.replace(this.patterns.email, "[REDACTED EMAIL]");
3072
+ safeContent = safeContent.replace(this.patterns.ssn, "[REDACTED SSN]");
3073
+ return { ...msg, content: safeContent };
3074
+ }
3075
+ return msg;
3076
+ });
3077
+ return context;
3078
+ }
3079
+ };
3080
+ var MaxContextOverflowPlugin = class {
3081
+ constructor(maxMessages = 20) {
3082
+ this.maxMessages = maxMessages;
3083
+ this.name = "MaxContextOverflowPlugin";
3084
+ }
3085
+ async beforeRequest(context) {
3086
+ if (context.messages.length > this.maxMessages) {
3087
+ const hasSystem = context.messages[0]?.role === "system";
3088
+ const systemMsg = hasSystem ? context.messages[0] : null;
3089
+ const recentMsgs = context.messages.slice(-(this.maxMessages - (hasSystem ? 1 : 0)));
3090
+ context.messages = hasSystem && systemMsg ? [systemMsg, ...recentMsgs] : recentMsgs;
3091
+ console.warn(`[MaxContextOverflowPlugin] Truncated chat history to ${this.maxMessages} messages to preserve context limits.`);
3092
+ }
3093
+ return context;
3094
+ }
3095
+ };
3096
+ var LoggerPlugin = class {
3097
+ constructor() {
3098
+ this.name = "LoggerPlugin";
3099
+ }
3100
+ onInit() {
3101
+ console.log("[LoggerPlugin] Tekimax SDK initialized with Logger Plugin active.");
3102
+ }
3103
+ async beforeRequest(context) {
3104
+ console.log(`[LoggerPlugin] Sending request to model: ${context.model} (${context.messages.length} messages)`);
3105
+ }
3106
+ async afterResponse(context, result) {
3107
+ console.log(`[LoggerPlugin] Received completion from ${context.model}. Usage:`, result.usage);
3108
+ }
3109
+ onStreamChunk(context, chunk) {
3110
+ if (chunk.usage) {
3111
+ console.log(`[LoggerPlugin] Stream completed. Final usage:`, chunk.usage);
3112
+ }
3113
+ }
3114
+ async beforeToolExecute(toolName, args) {
3115
+ console.log(`[LoggerPlugin] Executing tool '${toolName}' with args:`, args);
3116
+ }
3117
+ async afterToolExecute(toolName, result) {
3118
+ console.log(`[LoggerPlugin] Tool '${toolName}' returned successfully.`);
3119
+ }
3120
+ };
2999
3121
  // Annotate the CommonJS export names for ESM import in node:
3000
3122
  0 && (module.exports = {
3001
3123
  AnthropicProvider,
@@ -3004,9 +3126,12 @@ var Tekimax = class {
3004
3126
  FallbackProvider,
3005
3127
  GeminiProvider,
3006
3128
  GrokProvider,
3129
+ LoggerPlugin,
3130
+ MaxContextOverflowPlugin,
3007
3131
  OllamaProvider,
3008
3132
  OpenAIProvider,
3009
3133
  OpenRouterProvider,
3134
+ PIIFilterPlugin,
3010
3135
  RateLimiter,
3011
3136
  ResponseCache,
3012
3137
  SessionStore,