ai-functions 2.1.3 → 2.3.0
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +55 -1
- package/README.md +38 -0
- package/dist/ai-promise.d.ts +3 -3
- package/dist/ai-promise.d.ts.map +1 -1
- package/dist/ai-promise.js +135 -64
- package/dist/ai-promise.js.map +1 -1
- package/dist/ai-schemas.d.ts +56 -0
- package/dist/ai-schemas.d.ts.map +1 -0
- package/dist/ai-schemas.js +53 -0
- package/dist/ai-schemas.js.map +1 -0
- package/dist/ai.d.ts +16 -242
- package/dist/ai.d.ts.map +1 -1
- package/dist/ai.js +51 -858
- package/dist/ai.js.map +1 -1
- package/dist/batch/anthropic.d.ts +6 -4
- package/dist/batch/anthropic.d.ts.map +1 -1
- package/dist/batch/anthropic.js +83 -145
- package/dist/batch/anthropic.js.map +1 -1
- package/dist/batch/bedrock.d.ts +8 -30
- package/dist/batch/bedrock.d.ts.map +1 -1
- package/dist/batch/bedrock.js +155 -338
- package/dist/batch/bedrock.js.map +1 -1
- package/dist/batch/cloudflare.d.ts +8 -20
- package/dist/batch/cloudflare.d.ts.map +1 -1
- package/dist/batch/cloudflare.js +68 -189
- package/dist/batch/cloudflare.js.map +1 -1
- package/dist/batch/google.d.ts +6 -20
- package/dist/batch/google.d.ts.map +1 -1
- package/dist/batch/google.js +70 -238
- package/dist/batch/google.js.map +1 -1
- package/dist/batch/index.d.ts +4 -1
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js +4 -1
- package/dist/batch/index.js.map +1 -1
- package/dist/batch/memory.d.ts +1 -1
- package/dist/batch/memory.d.ts.map +1 -1
- package/dist/batch/memory.js +14 -10
- package/dist/batch/memory.js.map +1 -1
- package/dist/batch/openai.d.ts +11 -14
- package/dist/batch/openai.d.ts.map +1 -1
- package/dist/batch/openai.js +52 -156
- package/dist/batch/openai.js.map +1 -1
- package/dist/batch/provider.d.ts +111 -0
- package/dist/batch/provider.d.ts.map +1 -0
- package/dist/batch/provider.js +233 -0
- package/dist/batch/provider.js.map +1 -0
- package/dist/batch-map.d.ts.map +1 -1
- package/dist/batch-map.js +23 -17
- package/dist/batch-map.js.map +1 -1
- package/dist/batch-queue.d.ts +65 -0
- package/dist/batch-queue.d.ts.map +1 -1
- package/dist/batch-queue.js +169 -14
- package/dist/batch-queue.js.map +1 -1
- package/dist/budget.d.ts.map +1 -1
- package/dist/budget.js +27 -14
- package/dist/budget.js.map +1 -1
- package/dist/cache.d.ts +23 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +36 -15
- package/dist/cache.js.map +1 -1
- package/dist/context.d.ts +26 -8
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +64 -62
- package/dist/context.js.map +1 -1
- package/dist/digital-objects-registry.d.ts +229 -0
- package/dist/digital-objects-registry.d.ts.map +1 -0
- package/dist/digital-objects-registry.js +617 -0
- package/dist/digital-objects-registry.js.map +1 -0
- package/dist/embeddings.d.ts +2 -2
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +35 -0
- package/dist/errors.js.map +1 -0
- package/dist/eval/runner.d.ts +8 -0
- package/dist/eval/runner.d.ts.map +1 -1
- package/dist/eval/runner.js +41 -35
- package/dist/eval/runner.js.map +1 -1
- package/dist/eval-log/in-memory.d.ts +34 -0
- package/dist/eval-log/in-memory.d.ts.map +1 -0
- package/dist/eval-log/in-memory.js +84 -0
- package/dist/eval-log/in-memory.js.map +1 -0
- package/dist/eval-log/index.d.ts +29 -0
- package/dist/eval-log/index.d.ts.map +1 -0
- package/dist/eval-log/index.js +39 -0
- package/dist/eval-log/index.js.map +1 -0
- package/dist/eval-log/types.d.ts +101 -0
- package/dist/eval-log/types.d.ts.map +1 -0
- package/dist/eval-log/types.js +16 -0
- package/dist/eval-log/types.js.map +1 -0
- package/dist/function-registry.d.ts +116 -0
- package/dist/function-registry.d.ts.map +1 -0
- package/dist/function-registry.js +546 -0
- package/dist/function-registry.js.map +1 -0
- package/dist/generate.d.ts +9 -3
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +18 -18
- package/dist/generate.js.map +1 -1
- package/dist/index.d.ts +18 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -18
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +118 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +187 -0
- package/dist/logger.js.map +1 -0
- package/dist/middleware/budget.d.ts +84 -0
- package/dist/middleware/budget.d.ts.map +1 -0
- package/dist/middleware/budget.js +110 -0
- package/dist/middleware/budget.js.map +1 -0
- package/dist/middleware/cache.d.ts +103 -0
- package/dist/middleware/cache.d.ts.map +1 -0
- package/dist/middleware/cache.js +228 -0
- package/dist/middleware/cache.js.map +1 -0
- package/dist/middleware/embed-cache.d.ts +99 -0
- package/dist/middleware/embed-cache.d.ts.map +1 -0
- package/dist/middleware/embed-cache.js +128 -0
- package/dist/middleware/embed-cache.js.map +1 -0
- package/dist/middleware/index.d.ts +11 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +11 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/trace.d.ts +103 -0
- package/dist/middleware/trace.d.ts.map +1 -0
- package/dist/middleware/trace.js +176 -0
- package/dist/middleware/trace.js.map +1 -0
- package/dist/primitives.d.ts +120 -1
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js +398 -26
- package/dist/primitives.js.map +1 -1
- package/dist/retry.d.ts +66 -1
- package/dist/retry.d.ts.map +1 -1
- package/dist/retry.js +115 -8
- package/dist/retry.js.map +1 -1
- package/dist/schema.js +2 -2
- package/dist/schema.js.map +1 -1
- package/dist/telemetry.d.ts +128 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +285 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/template.d.ts.map +1 -1
- package/dist/template.js +6 -1
- package/dist/template.js.map +1 -1
- package/dist/tool-orchestration.d.ts +66 -4
- package/dist/tool-orchestration.d.ts.map +1 -1
- package/dist/tool-orchestration.js +123 -23
- package/dist/tool-orchestration.js.map +1 -1
- package/dist/type-guards.d.ts +28 -0
- package/dist/type-guards.d.ts.map +1 -0
- package/dist/type-guards.js +29 -0
- package/dist/type-guards.js.map +1 -0
- package/dist/types.d.ts +135 -17
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +36 -1
- package/dist/types.js.map +1 -1
- package/dist/wrap-for-v3.d.ts +80 -0
- package/dist/wrap-for-v3.d.ts.map +1 -0
- package/dist/wrap-for-v3.js +89 -0
- package/dist/wrap-for-v3.js.map +1 -0
- package/examples/00-quickstart.ts +232 -0
- package/examples/01-rag-chatbot.ts +212 -0
- package/examples/02-multi-agent-research.ts +290 -0
- package/examples/03-email-classification.ts +379 -0
- package/examples/04-content-moderation.ts +400 -0
- package/examples/05-document-extraction.ts +455 -0
- package/examples/06-streaming-chat-nextjs.ts +437 -0
- package/examples/07-cloudflare-worker.ts +483 -0
- package/examples/08-batch-processing.ts +491 -0
- package/examples/09-budget-constrained.ts +527 -0
- package/examples/10-tool-orchestration.ts +565 -0
- package/examples/11-retry-resilience.ts +403 -0
- package/examples/12-caching-strategies.ts +422 -0
- package/examples/README.md +145 -0
- package/package.json +28 -25
- package/src/ai-promise.ts +226 -140
- package/src/ai-schemas.ts +122 -0
- package/src/ai.ts +69 -1176
- package/src/batch/anthropic.ts +96 -161
- package/src/batch/bedrock.ts +203 -454
- package/src/batch/cloudflare.ts +99 -282
- package/src/batch/google.ts +91 -297
- package/src/batch/index.ts +4 -1
- package/src/batch/memory.ts +15 -10
- package/src/batch/openai.ts +65 -193
- package/src/batch/provider.ts +336 -0
- package/src/batch-map.ts +29 -24
- package/src/batch-queue.ts +200 -11
- package/src/budget.ts +31 -18
- package/src/cache.ts +45 -17
- package/src/context.ts +106 -77
- package/src/digital-objects-registry.ts +750 -0
- package/src/errors.ts +37 -0
- package/src/eval/runner.ts +60 -36
- package/src/eval-log/in-memory.ts +90 -0
- package/src/eval-log/index.ts +46 -0
- package/src/eval-log/types.ts +110 -0
- package/src/function-registry.ts +671 -0
- package/src/generate.ts +33 -28
- package/src/index.ts +119 -21
- package/src/logger.ts +232 -0
- package/src/middleware/budget.ts +171 -0
- package/src/middleware/cache.ts +299 -0
- package/src/middleware/embed-cache.ts +195 -0
- package/src/middleware/index.ts +23 -0
- package/src/middleware/trace.ts +248 -0
- package/src/primitives.ts +589 -62
- package/src/retry.ts +144 -18
- package/src/schema.ts +8 -8
- package/src/telemetry.ts +403 -0
- package/src/template.ts +8 -4
- package/src/tool-orchestration.ts +213 -48
- package/src/type-guards.ts +31 -0
- package/src/types.ts +164 -25
- package/src/wrap-for-v3.ts +105 -0
- package/test/ai-promise.test.ts +1080 -0
- package/test/ai-proxy.test.ts +1 -1
- package/test/batch-autosubmit-errors.test.ts +49 -37
- package/test/batch-blog-posts.test.ts +87 -129
- package/test/core-functions.test.ts +183 -579
- package/test/decide.test.ts +154 -322
- package/test/define.test.ts +211 -8
- package/test/digital-objects-registry.test.ts +760 -0
- package/test/embedding-cache-middleware.test.ts +140 -0
- package/test/generate-core.test.ts +140 -229
- package/test/implicit-batch.test.ts +22 -65
- package/test/retry-policy-integration.test.ts +117 -0
- package/test/schema.test.ts +55 -19
- package/test/template.test.ts +1164 -0
- package/test/tool-orchestration.test.ts +270 -0
- package/test/wrap-for-v3.test.ts +612 -0
- package/vitest.config.js +6 -0
- package/vitest.config.ts +20 -0
- package/LICENSE +0 -21
- package/dist/rpc/auth.d.ts +0 -69
- package/dist/rpc/auth.d.ts.map +0 -1
- package/dist/rpc/auth.js +0 -136
- package/dist/rpc/auth.js.map +0 -1
- package/dist/rpc/client.d.ts +0 -62
- package/dist/rpc/client.d.ts.map +0 -1
- package/dist/rpc/client.js +0 -103
- package/dist/rpc/client.js.map +0 -1
- package/dist/rpc/deferred.d.ts +0 -60
- package/dist/rpc/deferred.d.ts.map +0 -1
- package/dist/rpc/deferred.js +0 -96
- package/dist/rpc/deferred.js.map +0 -1
- package/dist/rpc/index.d.ts +0 -22
- package/dist/rpc/index.d.ts.map +0 -1
- package/dist/rpc/index.js +0 -38
- package/dist/rpc/index.js.map +0 -1
- package/dist/rpc/local.d.ts +0 -42
- package/dist/rpc/local.d.ts.map +0 -1
- package/dist/rpc/local.js +0 -50
- package/dist/rpc/local.js.map +0 -1
- package/dist/rpc/server.d.ts +0 -165
- package/dist/rpc/server.d.ts.map +0 -1
- package/dist/rpc/server.js +0 -405
- package/dist/rpc/server.js.map +0 -1
- package/dist/rpc/session.d.ts +0 -32
- package/dist/rpc/session.d.ts.map +0 -1
- package/dist/rpc/session.js +0 -43
- package/dist/rpc/session.js.map +0 -1
- package/dist/rpc/transport.d.ts +0 -306
- package/dist/rpc/transport.d.ts.map +0 -1
- package/dist/rpc/transport.js +0 -731
- package/dist/rpc/transport.js.map +0 -1
- package/src/batch/anthropic.js +0 -256
- package/src/batch/bedrock.js +0 -584
- package/src/batch/cloudflare.js +0 -287
- package/src/batch/google.js +0 -359
- package/src/batch/index.js +0 -30
- package/src/batch/memory.js +0 -187
- package/src/batch/openai.js +0 -402
- package/src/eval/index.js +0 -7
- package/src/eval/models.js +0 -119
- package/src/eval/runner.js +0 -147
- package/test/schema.test.js +0 -96
|
@@ -768,3 +768,273 @@ describe('Token Usage Tracking', () => {
|
|
|
768
768
|
expect(result.usage!.totalTokens).toBe(160)
|
|
769
769
|
})
|
|
770
770
|
})
|
|
771
|
+
|
|
772
|
+
// ============================================================================
|
|
773
|
+
// cachedTool Tests - Tool result caching with cleanup
|
|
774
|
+
// ============================================================================
|
|
775
|
+
|
|
776
|
+
import { cachedTool, type CachedTool } from '../src/tool-orchestration.js'
|
|
777
|
+
|
|
778
|
+
describe('cachedTool', () => {
|
|
779
|
+
describe('basic caching behavior', () => {
|
|
780
|
+
it('should cache tool results', async () => {
|
|
781
|
+
let executionCount = 0
|
|
782
|
+
const countingTool: Tool = {
|
|
783
|
+
name: 'counting',
|
|
784
|
+
description: 'Counts executions',
|
|
785
|
+
parameters: z.object({ key: z.string() }),
|
|
786
|
+
execute: async ({ key }) => {
|
|
787
|
+
executionCount++
|
|
788
|
+
return { key, count: executionCount }
|
|
789
|
+
},
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
const cached = cachedTool(countingTool, { ttl: 1000 })
|
|
793
|
+
|
|
794
|
+
// First call executes the tool
|
|
795
|
+
const result1 = await cached.execute({ key: 'test' })
|
|
796
|
+
expect(result1).toEqual({ key: 'test', count: 1 })
|
|
797
|
+
|
|
798
|
+
// Second call with same key returns cached result
|
|
799
|
+
const result2 = await cached.execute({ key: 'test' })
|
|
800
|
+
expect(result2).toEqual({ key: 'test', count: 1 })
|
|
801
|
+
expect(executionCount).toBe(1) // Only executed once
|
|
802
|
+
|
|
803
|
+
// Different key executes again
|
|
804
|
+
const result3 = await cached.execute({ key: 'other' })
|
|
805
|
+
expect(result3).toEqual({ key: 'other', count: 2 })
|
|
806
|
+
})
|
|
807
|
+
|
|
808
|
+
it('should expire cached entries after TTL', async () => {
|
|
809
|
+
vi.useFakeTimers()
|
|
810
|
+
let executionCount = 0
|
|
811
|
+
const countingTool: Tool = {
|
|
812
|
+
name: 'counting',
|
|
813
|
+
description: 'Counts executions',
|
|
814
|
+
parameters: z.object({ key: z.string() }),
|
|
815
|
+
execute: async ({ key }) => {
|
|
816
|
+
executionCount++
|
|
817
|
+
return { key, count: executionCount }
|
|
818
|
+
},
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
const cached = cachedTool(countingTool, { ttl: 100 })
|
|
822
|
+
|
|
823
|
+
// First call
|
|
824
|
+
await cached.execute({ key: 'test' })
|
|
825
|
+
expect(executionCount).toBe(1)
|
|
826
|
+
|
|
827
|
+
// After TTL expires
|
|
828
|
+
vi.advanceTimersByTime(150)
|
|
829
|
+
|
|
830
|
+
// Should execute again since cache expired
|
|
831
|
+
await cached.execute({ key: 'test' })
|
|
832
|
+
expect(executionCount).toBe(2)
|
|
833
|
+
|
|
834
|
+
vi.useRealTimers()
|
|
835
|
+
})
|
|
836
|
+
})
|
|
837
|
+
|
|
838
|
+
describe('cache cleanup', () => {
|
|
839
|
+
it('should periodically clean up expired entries', async () => {
|
|
840
|
+
vi.useFakeTimers()
|
|
841
|
+
let executionCount = 0
|
|
842
|
+
const countingTool: Tool = {
|
|
843
|
+
name: 'counting',
|
|
844
|
+
description: 'Counts executions',
|
|
845
|
+
parameters: z.object({ key: z.string() }),
|
|
846
|
+
execute: async ({ key }) => {
|
|
847
|
+
executionCount++
|
|
848
|
+
return { key, count: executionCount }
|
|
849
|
+
},
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
const cached = cachedTool(countingTool, {
|
|
853
|
+
ttl: 100,
|
|
854
|
+
cleanupIntervalMs: 50,
|
|
855
|
+
}) as CachedTool
|
|
856
|
+
|
|
857
|
+
// Execute to populate cache
|
|
858
|
+
await cached.execute({ key: 'entry1' })
|
|
859
|
+
await cached.execute({ key: 'entry2' })
|
|
860
|
+
|
|
861
|
+
expect(cached.cacheSize()).toBe(2)
|
|
862
|
+
|
|
863
|
+
// Wait for TTL to expire and cleanup to run
|
|
864
|
+
vi.advanceTimersByTime(150)
|
|
865
|
+
|
|
866
|
+
// Entries should be cleaned up automatically
|
|
867
|
+
expect(cached.cacheSize()).toBe(0)
|
|
868
|
+
|
|
869
|
+
// Cleanup timer
|
|
870
|
+
cached.destroy()
|
|
871
|
+
vi.useRealTimers()
|
|
872
|
+
})
|
|
873
|
+
|
|
874
|
+
it('should stop cleanup timer and clear cache when destroyed', async () => {
|
|
875
|
+
vi.useFakeTimers()
|
|
876
|
+
const countingTool: Tool = {
|
|
877
|
+
name: 'counting',
|
|
878
|
+
description: 'Counts executions',
|
|
879
|
+
parameters: z.object({ key: z.string() }),
|
|
880
|
+
execute: async ({ key }) => ({ key }),
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
const cached = cachedTool(countingTool, {
|
|
884
|
+
ttl: 100,
|
|
885
|
+
cleanupIntervalMs: 50,
|
|
886
|
+
}) as CachedTool
|
|
887
|
+
|
|
888
|
+
await cached.execute({ key: 'entry1' })
|
|
889
|
+
expect(cached.cacheSize()).toBe(1)
|
|
890
|
+
|
|
891
|
+
// Destroy stops cleanup timer and clears cache to prevent memory leaks
|
|
892
|
+
cached.destroy()
|
|
893
|
+
|
|
894
|
+
// Cache should be cleared immediately on destroy
|
|
895
|
+
expect(cached.cacheSize()).toBe(0)
|
|
896
|
+
|
|
897
|
+
// Advancing time should have no effect (timer is stopped)
|
|
898
|
+
vi.advanceTimersByTime(150)
|
|
899
|
+
expect(cached.cacheSize()).toBe(0)
|
|
900
|
+
|
|
901
|
+
vi.useRealTimers()
|
|
902
|
+
})
|
|
903
|
+
|
|
904
|
+
it('should clear all cache entries on clearCache()', async () => {
|
|
905
|
+
const countingTool: Tool = {
|
|
906
|
+
name: 'counting',
|
|
907
|
+
description: 'Counts executions',
|
|
908
|
+
parameters: z.object({ key: z.string() }),
|
|
909
|
+
execute: async ({ key }) => ({ key }),
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
const cached = cachedTool(countingTool, { ttl: 60000 }) as CachedTool
|
|
913
|
+
|
|
914
|
+
await cached.execute({ key: 'entry1' })
|
|
915
|
+
await cached.execute({ key: 'entry2' })
|
|
916
|
+
await cached.execute({ key: 'entry3' })
|
|
917
|
+
|
|
918
|
+
expect(cached.cacheSize()).toBe(3)
|
|
919
|
+
|
|
920
|
+
cached.clearCache()
|
|
921
|
+
expect(cached.cacheSize()).toBe(0)
|
|
922
|
+
|
|
923
|
+
cached.destroy()
|
|
924
|
+
})
|
|
925
|
+
})
|
|
926
|
+
|
|
927
|
+
describe('max cache size (LRU eviction)', () => {
|
|
928
|
+
it('should evict oldest entries when maxSize is reached', async () => {
|
|
929
|
+
vi.useFakeTimers()
|
|
930
|
+
let executionCount = 0
|
|
931
|
+
const countingTool: Tool = {
|
|
932
|
+
name: 'counting',
|
|
933
|
+
description: 'Counts executions',
|
|
934
|
+
parameters: z.object({ key: z.string() }),
|
|
935
|
+
execute: async ({ key }) => {
|
|
936
|
+
executionCount++
|
|
937
|
+
return { key, count: executionCount }
|
|
938
|
+
},
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
const cached = cachedTool(countingTool, {
|
|
942
|
+
ttl: 60000,
|
|
943
|
+
maxSize: 3,
|
|
944
|
+
}) as CachedTool
|
|
945
|
+
|
|
946
|
+
// Fill cache to max
|
|
947
|
+
await cached.execute({ key: 'a' })
|
|
948
|
+
vi.advanceTimersByTime(10)
|
|
949
|
+
await cached.execute({ key: 'b' })
|
|
950
|
+
vi.advanceTimersByTime(10)
|
|
951
|
+
await cached.execute({ key: 'c' })
|
|
952
|
+
vi.advanceTimersByTime(10)
|
|
953
|
+
|
|
954
|
+
expect(cached.cacheSize()).toBe(3)
|
|
955
|
+
expect(executionCount).toBe(3)
|
|
956
|
+
|
|
957
|
+
// Adding 4th entry should evict oldest ('a')
|
|
958
|
+
await cached.execute({ key: 'd' })
|
|
959
|
+
expect(cached.cacheSize()).toBe(3)
|
|
960
|
+
|
|
961
|
+
// Accessing 'a' should re-execute since it was evicted
|
|
962
|
+
await cached.execute({ key: 'a' })
|
|
963
|
+
expect(executionCount).toBe(5) // New execution
|
|
964
|
+
|
|
965
|
+
cached.destroy()
|
|
966
|
+
vi.useRealTimers()
|
|
967
|
+
})
|
|
968
|
+
|
|
969
|
+
it('should update LRU order on cache hit', async () => {
|
|
970
|
+
vi.useFakeTimers()
|
|
971
|
+
let executionCount = 0
|
|
972
|
+
const countingTool: Tool = {
|
|
973
|
+
name: 'counting',
|
|
974
|
+
description: 'Counts executions',
|
|
975
|
+
parameters: z.object({ key: z.string() }),
|
|
976
|
+
execute: async ({ key }) => {
|
|
977
|
+
executionCount++
|
|
978
|
+
return { key, count: executionCount }
|
|
979
|
+
},
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
const cached = cachedTool(countingTool, {
|
|
983
|
+
ttl: 60000,
|
|
984
|
+
maxSize: 3,
|
|
985
|
+
}) as CachedTool
|
|
986
|
+
|
|
987
|
+
// Fill cache: a, b, c (oldest to newest)
|
|
988
|
+
await cached.execute({ key: 'a' })
|
|
989
|
+
vi.advanceTimersByTime(10)
|
|
990
|
+
await cached.execute({ key: 'b' })
|
|
991
|
+
vi.advanceTimersByTime(10)
|
|
992
|
+
await cached.execute({ key: 'c' })
|
|
993
|
+
vi.advanceTimersByTime(10)
|
|
994
|
+
|
|
995
|
+
// Access 'a' to make it recently used
|
|
996
|
+
await cached.execute({ key: 'a' }) // Cache hit
|
|
997
|
+
vi.advanceTimersByTime(10)
|
|
998
|
+
expect(executionCount).toBe(3) // No new execution
|
|
999
|
+
|
|
1000
|
+
// Add 'd' - should evict 'b' (now oldest) not 'a'
|
|
1001
|
+
await cached.execute({ key: 'd' })
|
|
1002
|
+
|
|
1003
|
+
// 'b' was evicted, 'a' and 'c' remain
|
|
1004
|
+
await cached.execute({ key: 'b' }) // Should re-execute
|
|
1005
|
+
expect(executionCount).toBe(5)
|
|
1006
|
+
|
|
1007
|
+
await cached.execute({ key: 'a' }) // Still cached
|
|
1008
|
+
expect(executionCount).toBe(5)
|
|
1009
|
+
|
|
1010
|
+
cached.destroy()
|
|
1011
|
+
vi.useRealTimers()
|
|
1012
|
+
})
|
|
1013
|
+
})
|
|
1014
|
+
|
|
1015
|
+
describe('resource cleanup on tool destruction', () => {
|
|
1016
|
+
it('should clean up timers and memory when destroy is called', async () => {
|
|
1017
|
+
const tool: Tool = {
|
|
1018
|
+
name: 'test',
|
|
1019
|
+
description: 'Test tool',
|
|
1020
|
+
parameters: z.object({ key: z.string() }),
|
|
1021
|
+
execute: async ({ key }) => ({ key }),
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
const cached = cachedTool(tool, {
|
|
1025
|
+
ttl: 1000,
|
|
1026
|
+
cleanupIntervalMs: 100,
|
|
1027
|
+
}) as CachedTool
|
|
1028
|
+
|
|
1029
|
+
await cached.execute({ key: 'test' })
|
|
1030
|
+
expect(cached.cacheSize()).toBe(1)
|
|
1031
|
+
|
|
1032
|
+
cached.destroy()
|
|
1033
|
+
expect(cached.cacheSize()).toBe(0)
|
|
1034
|
+
|
|
1035
|
+
// Should still work after destroy but without caching
|
|
1036
|
+
await cached.execute({ key: 'test2' })
|
|
1037
|
+
expect(cached.cacheSize()).toBe(0)
|
|
1038
|
+
})
|
|
1039
|
+
})
|
|
1040
|
+
})
|