@ai.ntellect/core 0.8.3 → 1.1.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/.mocharc.json +2 -1
- package/AGENTS.md +50 -0
- package/ARCHITECTURE.md +58 -0
- package/BENCHMARK.md +38 -0
- package/CHANGELOG.md +224 -0
- package/README.md +413 -199
- package/agent/agent-workflow.ts +569 -0
- package/agent/agent.ts +183 -0
- package/{modules/agent → agent}/base/executor.ts +11 -8
- package/{modules/agent → agent}/base/index.ts +2 -14
- package/agent/entity-resolver/confidence.ts +54 -0
- package/agent/entity-resolver/entity-index.ts +241 -0
- package/agent/entity-resolver/explore-strategy.ts +82 -0
- package/agent/entity-resolver/index.ts +22 -0
- package/agent/entity-resolver/resolver.ts +193 -0
- package/agent/entity-resolver/scorer.ts +211 -0
- package/agent/entity-resolver/types.ts +98 -0
- package/agent/generic-executor.ts +713 -0
- package/agent/handlers/cognitive-handler.ts +197 -0
- package/agent/handlers/index.ts +1 -0
- package/agent/handoff.ts +58 -0
- package/agent/llm-factory.ts +343 -0
- package/agent/orchestrator.ts +45 -0
- package/{modules/agent → agent}/prompt-builder.ts +1 -1
- package/agent/registry.ts +64 -0
- package/agent/tools/file-system.ts +471 -0
- package/agent/tools/index.ts +23 -0
- package/agent/tools/logger.ts +105 -0
- package/agent/tools/tool-resolver.ts +104 -0
- package/benchmark/cortexflow-workflow.ts +182 -0
- package/benchmark/langgraph-workflow.ts +201 -0
- package/benchmark/llm-client.ts +76 -0
- package/benchmark/run-benchmark.ts +222 -0
- package/cli-dev.ts +419 -0
- package/dist/agent/agent-workflow.d.ts +41 -0
- package/dist/agent/agent-workflow.d.ts.map +1 -0
- package/dist/agent/agent-workflow.js +560 -0
- package/dist/agent/agent-workflow.js.map +1 -0
- package/dist/agent/agent.d.ts +38 -0
- package/dist/agent/agent.d.ts.map +1 -0
- package/dist/agent/agent.js +149 -0
- package/dist/agent/agent.js.map +1 -0
- package/dist/{modules/agent → agent}/base/executor.d.ts +3 -3
- package/dist/agent/base/executor.d.ts.map +1 -0
- package/dist/{modules/agent → agent}/base/executor.js +8 -5
- package/dist/agent/base/executor.js.map +1 -0
- package/dist/{modules/agent → agent}/base/index.d.ts +1 -1
- package/dist/agent/base/index.d.ts.map +1 -0
- package/dist/{modules/agent → agent}/base/index.js +0 -12
- package/dist/agent/base/index.js.map +1 -0
- package/dist/agent/entity-resolver/confidence.d.ts +4 -0
- package/dist/agent/entity-resolver/confidence.d.ts.map +1 -0
- package/dist/agent/entity-resolver/confidence.js +47 -0
- package/dist/agent/entity-resolver/confidence.js.map +1 -0
- package/dist/agent/entity-resolver/entity-index.d.ts +40 -0
- package/dist/agent/entity-resolver/entity-index.d.ts.map +1 -0
- package/dist/agent/entity-resolver/entity-index.js +244 -0
- package/dist/agent/entity-resolver/entity-index.js.map +1 -0
- package/dist/agent/entity-resolver/explore-strategy.d.ts +13 -0
- package/dist/agent/entity-resolver/explore-strategy.d.ts.map +1 -0
- package/dist/agent/entity-resolver/explore-strategy.js +109 -0
- package/dist/agent/entity-resolver/explore-strategy.js.map +1 -0
- package/dist/agent/entity-resolver/index.d.ts +8 -0
- package/dist/agent/entity-resolver/index.d.ts.map +1 -0
- package/dist/agent/entity-resolver/index.js +16 -0
- package/dist/agent/entity-resolver/index.js.map +1 -0
- package/dist/agent/entity-resolver/resolver.d.ts +18 -0
- package/dist/agent/entity-resolver/resolver.d.ts.map +1 -0
- package/dist/agent/entity-resolver/resolver.js +158 -0
- package/dist/agent/entity-resolver/resolver.js.map +1 -0
- package/dist/agent/entity-resolver/scorer.d.ts +3 -0
- package/dist/agent/entity-resolver/scorer.d.ts.map +1 -0
- package/dist/agent/entity-resolver/scorer.js +182 -0
- package/dist/agent/entity-resolver/scorer.js.map +1 -0
- package/dist/agent/entity-resolver/types.d.ts +89 -0
- package/dist/agent/entity-resolver/types.d.ts.map +1 -0
- package/dist/agent/entity-resolver/types.js +3 -0
- package/dist/agent/entity-resolver/types.js.map +1 -0
- package/dist/{modules/agent → agent}/generic-executor.d.ts +37 -27
- package/dist/agent/generic-executor.d.ts.map +1 -0
- package/dist/agent/generic-executor.js +620 -0
- package/dist/agent/generic-executor.js.map +1 -0
- package/dist/agent/handlers/cognitive-handler.d.ts +44 -0
- package/dist/agent/handlers/cognitive-handler.d.ts.map +1 -0
- package/dist/agent/handlers/cognitive-handler.js +172 -0
- package/dist/agent/handlers/cognitive-handler.js.map +1 -0
- package/dist/agent/handlers/index.d.ts +2 -0
- package/dist/agent/handlers/index.d.ts.map +1 -0
- package/dist/agent/handlers/index.js +7 -0
- package/dist/agent/handlers/index.js.map +1 -0
- package/dist/agent/handoff.d.ts +12 -0
- package/dist/agent/handoff.d.ts.map +1 -0
- package/dist/agent/handoff.js +62 -0
- package/dist/agent/handoff.js.map +1 -0
- package/dist/agent/llm-factory.d.ts.map +1 -0
- package/dist/agent/llm-factory.js +297 -0
- package/dist/agent/llm-factory.js.map +1 -0
- package/dist/agent/orchestrator.d.ts +18 -0
- package/dist/agent/orchestrator.d.ts.map +1 -0
- package/dist/agent/orchestrator.js +44 -0
- package/dist/agent/orchestrator.js.map +1 -0
- package/dist/{modules/agent → agent}/prompt-builder.d.ts +1 -1
- package/dist/agent/prompt-builder.d.ts.map +1 -0
- package/dist/agent/prompt-builder.js.map +1 -0
- package/dist/agent/registry.d.ts +19 -0
- package/dist/agent/registry.d.ts.map +1 -0
- package/dist/agent/registry.js +48 -0
- package/dist/agent/registry.js.map +1 -0
- package/dist/agent/tools/file-system.d.ts +11 -0
- package/dist/agent/tools/file-system.d.ts.map +1 -0
- package/dist/agent/tools/file-system.js +528 -0
- package/dist/agent/tools/file-system.js.map +1 -0
- package/dist/agent/tools/index.d.ts +4 -0
- package/dist/agent/tools/index.d.ts.map +1 -0
- package/dist/agent/tools/index.js +20 -0
- package/dist/agent/tools/index.js.map +1 -0
- package/dist/agent/tools/logger.d.ts +33 -0
- package/dist/agent/tools/logger.d.ts.map +1 -0
- package/dist/agent/tools/logger.js +86 -0
- package/dist/agent/tools/logger.js.map +1 -0
- package/dist/agent/tools/tool-resolver.d.ts +7 -0
- package/dist/agent/tools/tool-resolver.d.ts.map +1 -0
- package/dist/agent/tools/tool-resolver.js +122 -0
- package/dist/agent/tools/tool-resolver.js.map +1 -0
- package/dist/execution/adapters/in-memory-checkpoint.d.ts +11 -0
- package/dist/execution/adapters/in-memory-checkpoint.d.ts.map +1 -0
- package/dist/execution/adapters/in-memory-checkpoint.js +54 -0
- package/dist/execution/adapters/in-memory-checkpoint.js.map +1 -0
- package/dist/execution/compiler.d.ts +8 -0
- package/dist/execution/compiler.d.ts.map +1 -0
- package/dist/execution/compiler.js +50 -0
- package/dist/execution/compiler.js.map +1 -0
- package/dist/execution/controller.d.ts.map +1 -0
- package/dist/execution/controller.js.map +1 -0
- package/dist/execution/event-manager.d.ts +29 -0
- package/dist/execution/event-manager.d.ts.map +1 -0
- package/dist/{graph → execution}/event-manager.js +4 -114
- package/dist/execution/event-manager.js.map +1 -0
- package/dist/{graph → execution}/index.d.ts +50 -2
- package/dist/execution/index.d.ts.map +1 -0
- package/dist/execution/index.js +638 -0
- package/dist/execution/index.js.map +1 -0
- package/dist/execution/logger.d.ts.map +1 -0
- package/dist/execution/logger.js.map +1 -0
- package/dist/{graph → execution}/node.d.ts +6 -1
- package/dist/execution/node.d.ts.map +1 -0
- package/dist/{graph → execution}/node.js +83 -10
- package/dist/execution/node.js.map +1 -0
- package/dist/execution/observer.d.ts.map +1 -0
- package/dist/execution/observer.js.map +1 -0
- package/dist/execution/planner.d.ts +14 -0
- package/dist/execution/planner.d.ts.map +1 -0
- package/dist/execution/planner.js +43 -0
- package/dist/execution/planner.js.map +1 -0
- package/dist/execution/reducer.d.ts +19 -0
- package/dist/execution/reducer.d.ts.map +1 -0
- package/dist/execution/reducer.js +61 -0
- package/dist/execution/reducer.js.map +1 -0
- package/dist/execution/registry.d.ts +38 -0
- package/dist/execution/registry.d.ts.map +1 -0
- package/dist/execution/registry.js +63 -0
- package/dist/execution/registry.js.map +1 -0
- package/dist/execution/send-api.d.ts +24 -0
- package/dist/execution/send-api.d.ts.map +1 -0
- package/dist/execution/send-api.js +52 -0
- package/dist/execution/send-api.js.map +1 -0
- package/dist/execution/types.parallel.d.ts +61 -0
- package/dist/execution/types.parallel.d.ts.map +1 -0
- package/dist/execution/types.parallel.js +3 -0
- package/dist/execution/types.parallel.js.map +1 -0
- package/dist/execution/visualizer.d.ts.map +1 -0
- package/dist/execution/visualizer.js.map +1 -0
- package/dist/index.d.ts +27 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +42 -8
- package/dist/index.js.map +1 -1
- package/dist/interfaces/index.d.ts +32 -1
- package/dist/interfaces/index.d.ts.map +1 -1
- package/dist/interfaces/index.js.map +1 -1
- package/dist/modules/cli/index.d.ts +13 -0
- package/dist/modules/cli/index.d.ts.map +1 -0
- package/dist/modules/cli/index.js +672 -0
- package/dist/modules/cli/index.js.map +1 -0
- package/dist/modules/nlp/engine.d.ts.map +1 -1
- package/dist/modules/nlp/engine.js +0 -1
- package/dist/modules/nlp/engine.js.map +1 -1
- package/dist/persistence/index.d.ts +12 -0
- package/dist/persistence/index.d.ts.map +1 -0
- package/dist/persistence/index.js +24 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/neo4j/driver.d.ts +26 -0
- package/dist/persistence/neo4j/driver.d.ts.map +1 -0
- package/dist/persistence/neo4j/driver.js +64 -0
- package/dist/persistence/neo4j/driver.js.map +1 -0
- package/dist/persistence/neo4j/entity-store.d.ts +15 -0
- package/dist/persistence/neo4j/entity-store.d.ts.map +1 -0
- package/dist/persistence/neo4j/entity-store.js +136 -0
- package/dist/persistence/neo4j/entity-store.js.map +1 -0
- package/dist/persistence/neo4j/execution-tracer.d.ts +19 -0
- package/dist/persistence/neo4j/execution-tracer.d.ts.map +1 -0
- package/dist/persistence/neo4j/execution-tracer.js +166 -0
- package/dist/persistence/neo4j/execution-tracer.js.map +1 -0
- package/dist/persistence/neo4j/memory-adapter.d.ts +35 -0
- package/dist/persistence/neo4j/memory-adapter.d.ts.map +1 -0
- package/dist/persistence/neo4j/memory-adapter.js +252 -0
- package/dist/persistence/neo4j/memory-adapter.js.map +1 -0
- package/dist/persistence/neo4j/petri-checkpoint-adapter.d.ts +22 -0
- package/dist/persistence/neo4j/petri-checkpoint-adapter.d.ts.map +1 -0
- package/dist/persistence/neo4j/petri-checkpoint-adapter.js +141 -0
- package/dist/persistence/neo4j/petri-checkpoint-adapter.js.map +1 -0
- package/dist/pipeline/agent-pipeline.d.ts +99 -0
- package/dist/pipeline/agent-pipeline.d.ts.map +1 -0
- package/dist/pipeline/agent-pipeline.js +356 -0
- package/dist/pipeline/agent-pipeline.js.map +1 -0
- package/dist/routing/checkpoint-adapter.d.ts +30 -0
- package/dist/routing/checkpoint-adapter.d.ts.map +1 -0
- package/dist/routing/checkpoint-adapter.js +83 -0
- package/dist/routing/checkpoint-adapter.js.map +1 -0
- package/dist/routing/documentation-generator.d.ts +47 -0
- package/dist/routing/documentation-generator.d.ts.map +1 -0
- package/dist/routing/documentation-generator.js +320 -0
- package/dist/routing/documentation-generator.js.map +1 -0
- package/dist/routing/index.d.ts +38 -0
- package/dist/routing/index.d.ts.map +1 -0
- package/dist/routing/index.js +375 -0
- package/dist/routing/index.js.map +1 -0
- package/dist/routing/intent-classifier.d.ts +206 -0
- package/dist/routing/intent-classifier.d.ts.map +1 -0
- package/dist/routing/intent-classifier.js +267 -0
- package/dist/routing/intent-classifier.js.map +1 -0
- package/dist/routing/matrix.d.ts +6 -0
- package/dist/routing/matrix.d.ts.map +1 -0
- package/dist/routing/matrix.js +131 -0
- package/dist/routing/matrix.js.map +1 -0
- package/dist/routing/orchestrator.d.ts +87 -0
- package/dist/routing/orchestrator.d.ts.map +1 -0
- package/dist/routing/orchestrator.js +425 -0
- package/dist/routing/orchestrator.js.map +1 -0
- package/dist/routing/postgres-checkpoint-adapter.d.ts +32 -0
- package/dist/routing/postgres-checkpoint-adapter.d.ts.map +1 -0
- package/dist/routing/postgres-checkpoint-adapter.js +167 -0
- package/dist/routing/postgres-checkpoint-adapter.js.map +1 -0
- package/dist/routing/redis-checkpoint-adapter.d.ts +34 -0
- package/dist/routing/redis-checkpoint-adapter.d.ts.map +1 -0
- package/dist/routing/redis-checkpoint-adapter.js +170 -0
- package/dist/routing/redis-checkpoint-adapter.js.map +1 -0
- package/dist/routing/types.d.ts +49 -0
- package/dist/routing/types.d.ts.map +1 -0
- package/dist/routing/types.js +3 -0
- package/dist/routing/types.js.map +1 -0
- package/dist/types/agent.d.ts +144 -88
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/agent.js +49 -0
- package/dist/types/agent.js.map +1 -1
- package/dist/types/index.d.ts +46 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/generate-action-schema.d.ts +2 -3
- package/dist/utils/generate-action-schema.d.ts.map +1 -1
- package/dist/utils/generate-action-schema.js +3 -1
- package/dist/utils/generate-action-schema.js.map +1 -1
- package/dist/utils/logger.d.ts +27 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +45 -0
- package/dist/utils/logger.js.map +1 -0
- package/docs/.gitbook/assets/image (1).png +0 -0
- package/docs/.gitbook/assets/image (2).png +0 -0
- package/docs/.gitbook/assets/image (3).png +0 -0
- package/docs/.gitbook/assets/image (4).png +0 -0
- package/docs/.gitbook/assets/image (5).png +0 -0
- package/docs/.gitbook/assets/image (6).png +0 -0
- package/docs/.gitbook/assets/image.png +0 -0
- package/docs/README.md +57 -0
- package/docs/SUMMARY.md +34 -0
- package/docs/cas-dusages.md +69 -0
- package/docs/cli/README.md +65 -0
- package/docs/concepts-cles.md +50 -0
- package/docs/core/architecture.md +87 -0
- package/docs/core/benchmark.md +59 -0
- package/docs/core/checkpoint.md +72 -0
- package/docs/core/documentation.md +55 -0
- package/docs/core/graphcontroller.md +63 -0
- package/docs/core/graphflow.md +137 -0
- package/docs/core/introduction.md +41 -0
- package/docs/core/les-evenements.md +95 -0
- package/docs/modules/agenda/README.md +63 -0
- package/docs/modules/agenda/interface-iagenda.md +170 -0
- package/docs/modules/agenda/les-adaptateurs/README.md +237 -0
- package/docs/modules/agenda/les-adaptateurs/nodecronadapter.md +91 -0
- package/docs/modules/introduction.md +55 -0
- package/docs/modules/les-adaptateurs.md +52 -0
- package/docs/modules/memoire/README.md +68 -0
- package/docs/modules/memoire/interface-imemory.md +183 -0
- package/docs/modules/memoire/les-adaptateurs/README.md +209 -0
- package/docs/modules/memoire/les-adaptateurs/inmemoryadapter.md +110 -0
- package/docs/modules/memoire/les-adaptateurs/meilisearchadapter.md +147 -0
- package/docs/modules/memoire/les-adaptateurs/redisadapter.md +212 -0
- package/docs/modules/nlp/README.md +44 -0
- package/docs/philosophie.md +51 -0
- package/docs/tutoriels/ajouter-des-conditions.md +150 -0
- package/docs/tutoriels/branching.md +194 -0
- package/docs/tutoriels/checkpoint-usage.md +99 -0
- package/docs/tutoriels/creer-agent-onchain.md +1041 -0
- package/docs/tutoriels/creer-un-agent.md +108 -0
- package/docs/tutoriels/creer-un-graphe-simple.md +92 -0
- package/docs/tutoriels/gerer-les-erreurs.md +124 -0
- package/docs/tutoriels/pour-commencer.md +73 -0
- package/docs/tutoriels/retry.md +166 -0
- package/execution/adapters/in-memory-checkpoint.ts +35 -0
- package/execution/compiler.ts +47 -0
- package/{graph → execution}/event-manager.ts +4 -117
- package/{graph → execution}/index.ts +376 -8
- package/{graph → execution}/node.ts +116 -12
- package/execution/planner.ts +40 -0
- package/execution/reducer.ts +73 -0
- package/execution/registry.ts +86 -0
- package/execution/send-api.ts +58 -0
- package/execution/types.parallel.ts +81 -0
- package/index.ts +35 -8
- package/interfaces/index.ts +37 -0
- package/modules/cli/index.ts +580 -0
- package/modules/nlp/engine.ts +0 -1
- package/package.json +50 -16
- package/persistence/index.ts +27 -0
- package/persistence/neo4j/driver.ts +34 -0
- package/persistence/neo4j/entity-store.ts +141 -0
- package/persistence/neo4j/execution-tracer.ts +194 -0
- package/persistence/neo4j/memory-adapter.ts +281 -0
- package/persistence/neo4j/petri-checkpoint-adapter.ts +153 -0
- package/pipeline/agent-pipeline.ts +426 -0
- package/routing/checkpoint-adapter.ts +79 -0
- package/routing/documentation-generator.ts +358 -0
- package/routing/index.ts +459 -0
- package/routing/intent-classifier.ts +360 -0
- package/routing/matrix.ts +138 -0
- package/routing/orchestrator.ts +498 -0
- package/routing/patterns/data-extraction.json +79 -0
- package/routing/patterns/human-approval.json +64 -0
- package/routing/patterns/rag-search.json +68 -0
- package/routing/postgres-checkpoint-adapter.ts +172 -0
- package/routing/redis-checkpoint-adapter.ts +187 -0
- package/routing/types.ts +59 -0
- package/routing/web-server.ts +260 -0
- package/scripts/generate-petri-docs.ts +70 -0
- package/scripts/get-gmail-token.js +65 -0
- package/scripts/get-gmail-token.ts +65 -0
- package/test/agent/agent.test.ts +92 -0
- package/test/agent/clone.test.ts +143 -0
- package/test/agent/cognitive-handler.test.ts +78 -0
- package/test/agent/entity-store.test.ts +80 -0
- package/test/agent/generic-executor.test.ts +230 -0
- package/test/agent/handoff.test.ts +163 -0
- package/test/agent/llm-factory.test.ts +40 -0
- package/test/agent/orchestrator.test.ts +156 -0
- package/test/agent/registry.test.ts +97 -0
- package/test/agent/tools.test.ts +267 -0
- package/test/execution/checkpoint.test.ts +811 -0
- package/test/{graph → execution}/controller.test.ts +2 -2
- package/test/{graph → execution}/event-manager.test.ts +1 -1
- package/test/{graph → execution}/index.test.ts +24 -15
- package/test/{graph → execution}/node.test.ts +3 -3
- package/test/{graph → execution}/observer.test.ts +2 -2
- package/test/execution/parallel.test.ts +135 -0
- package/test/execution/plan-llm-integration.test.ts +290 -0
- package/test/execution/plan-real-onchain.test.ts +226 -0
- package/test/execution/send-api.test.ts +121 -0
- package/test/modules/cli/index.test.ts +125 -0
- package/test/persistence/neo4j-execution-tracer.test.ts +96 -0
- package/test/persistence/neo4j-memory-adapter.test.ts +107 -0
- package/test/persistence/neo4j-petri-checkpoint.test.ts +89 -0
- package/test/pipeline/agent-pipeline.test.ts +118 -0
- package/test/routing/checkpoint-persistence.test.ts +58 -0
- package/test/routing/documentation-generator.test.ts +76 -0
- package/test/routing/integration.test.ts +261 -0
- package/test/routing/intent-classifier.test.ts +102 -0
- package/test/routing/petri.test.ts +156 -0
- package/test/routing/real-llm.test.ts +260 -0
- package/test-petri-features.ts +218 -0
- package/test-pipeline-api.ts +163 -0
- package/tsconfig.json +5 -2
- package/types/agent.ts +126 -4
- package/types/index.ts +66 -5
- package/utils/generate-action-schema.ts +5 -4
- package/utils/logger.ts +40 -0
- package/dist/graph/controller.d.ts.map +0 -1
- package/dist/graph/controller.js.map +0 -1
- package/dist/graph/event-manager.d.ts +0 -101
- package/dist/graph/event-manager.d.ts.map +0 -1
- package/dist/graph/event-manager.js.map +0 -1
- package/dist/graph/index.d.ts.map +0 -1
- package/dist/graph/index.js +0 -340
- package/dist/graph/index.js.map +0 -1
- package/dist/graph/logger.d.ts.map +0 -1
- package/dist/graph/logger.js.map +0 -1
- package/dist/graph/node.d.ts.map +0 -1
- package/dist/graph/node.js.map +0 -1
- package/dist/graph/observer.d.ts.map +0 -1
- package/dist/graph/observer.js.map +0 -1
- package/dist/graph/visualizer.d.ts.map +0 -1
- package/dist/graph/visualizer.js.map +0 -1
- package/dist/modules/agent/agent.d.ts +0 -35
- package/dist/modules/agent/agent.d.ts.map +0 -1
- package/dist/modules/agent/agent.js +0 -103
- package/dist/modules/agent/agent.js.map +0 -1
- package/dist/modules/agent/base/executor.d.ts.map +0 -1
- package/dist/modules/agent/base/executor.js.map +0 -1
- package/dist/modules/agent/base/index.d.ts.map +0 -1
- package/dist/modules/agent/base/index.js.map +0 -1
- package/dist/modules/agent/generic-assistant.d.ts +0 -20
- package/dist/modules/agent/generic-assistant.d.ts.map +0 -1
- package/dist/modules/agent/generic-assistant.js +0 -89
- package/dist/modules/agent/generic-assistant.js.map +0 -1
- package/dist/modules/agent/generic-executor.d.ts.map +0 -1
- package/dist/modules/agent/generic-executor.js +0 -189
- package/dist/modules/agent/generic-executor.js.map +0 -1
- package/dist/modules/agent/llm-factory.d.ts.map +0 -1
- package/dist/modules/agent/llm-factory.js +0 -56
- package/dist/modules/agent/llm-factory.js.map +0 -1
- package/dist/modules/agent/prompt-builder.d.ts.map +0 -1
- package/dist/modules/agent/prompt-builder.js.map +0 -1
- package/dist/modules/memory/adapters/in-memory/index.d.ts +0 -120
- package/dist/modules/memory/adapters/in-memory/index.d.ts.map +0 -1
- package/dist/modules/memory/adapters/in-memory/index.js +0 -212
- package/dist/modules/memory/adapters/in-memory/index.js.map +0 -1
- package/dist/modules/memory/adapters/meilisearch/index.d.ts +0 -110
- package/dist/modules/memory/adapters/meilisearch/index.d.ts.map +0 -1
- package/dist/modules/memory/adapters/meilisearch/index.js +0 -324
- package/dist/modules/memory/adapters/meilisearch/index.js.map +0 -1
- package/dist/modules/memory/adapters/redis/index.d.ts +0 -82
- package/dist/modules/memory/adapters/redis/index.d.ts.map +0 -1
- package/dist/modules/memory/adapters/redis/index.js +0 -160
- package/dist/modules/memory/adapters/redis/index.js.map +0 -1
- package/dist/modules/memory/index.d.ts +0 -67
- package/dist/modules/memory/index.d.ts.map +0 -1
- package/dist/modules/memory/index.js +0 -104
- package/dist/modules/memory/index.js.map +0 -1
- package/modules/agent/agent.ts +0 -107
- package/modules/agent/generic-assistant.ts +0 -92
- package/modules/agent/generic-executor.ts +0 -252
- package/modules/agent/llm-factory.ts +0 -47
- package/modules/memory/adapters/in-memory/index.ts +0 -208
- package/modules/memory/adapters/meilisearch/index.ts +0 -364
- package/modules/memory/adapters/redis/index.ts +0 -165
- package/modules/memory/index.ts +0 -93
- package/test/modules/memory/adapters/in-memory.test.ts +0 -153
- package/test/modules/memory/adapters/meilisearch.test.ts +0 -287
- package/test/modules/memory/base.test.ts +0 -230
- /package/dist/{modules/agent → agent}/llm-factory.d.ts +0 -0
- /package/dist/{modules/agent → agent}/prompt-builder.js +0 -0
- /package/dist/{graph → execution}/controller.d.ts +0 -0
- /package/dist/{graph → execution}/controller.js +0 -0
- /package/dist/{graph → execution}/logger.d.ts +0 -0
- /package/dist/{graph → execution}/logger.js +0 -0
- /package/dist/{graph → execution}/observer.d.ts +0 -0
- /package/dist/{graph → execution}/observer.js +0 -0
- /package/dist/{graph → execution}/visualizer.d.ts +0 -0
- /package/dist/{graph → execution}/visualizer.js +0 -0
- /package/{graph → execution}/controller.ts +0 -0
- /package/{graph → execution}/logger.ts +0 -0
- /package/{graph → execution}/observer.ts +0 -0
- /package/{graph → execution}/visualizer.ts +0 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
|
|
4
|
+
export type ResolvedEntity = {
|
|
5
|
+
type: "file" | "dir";
|
|
6
|
+
path: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export function resolvePath(userPath: string, cwd: string): ResolvedEntity[] {
|
|
10
|
+
const candidates: ResolvedEntity[] = [];
|
|
11
|
+
const absolutePath = path.isAbsolute(userPath) ? userPath : path.join(cwd, userPath);
|
|
12
|
+
|
|
13
|
+
if (fs.existsSync(absolutePath)) {
|
|
14
|
+
const stat = fs.statSync(absolutePath);
|
|
15
|
+
candidates.push({ type: stat.isDirectory() ? "dir" : "file", path: absolutePath });
|
|
16
|
+
return candidates;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (path.isAbsolute(userPath)) {
|
|
20
|
+
return candidates;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const parentDir = path.dirname(absolutePath);
|
|
24
|
+
const baseName = path.basename(absolutePath);
|
|
25
|
+
|
|
26
|
+
if (fs.existsSync(parentDir)) {
|
|
27
|
+
const entries = fs.readdirSync(parentDir);
|
|
28
|
+
const lowerTarget = baseName.toLowerCase();
|
|
29
|
+
|
|
30
|
+
for (const entry of entries) {
|
|
31
|
+
if (entry.toLowerCase() === lowerTarget) {
|
|
32
|
+
const fullPath = path.join(parentDir, entry);
|
|
33
|
+
const stat = fs.statSync(fullPath);
|
|
34
|
+
candidates.push({ type: stat.isDirectory() ? "dir" : "file", path: fullPath });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const noExt = baseName.includes(".") ? null : baseName;
|
|
39
|
+
if (noExt) {
|
|
40
|
+
for (const entry of entries) {
|
|
41
|
+
const entryNoExt = entry.includes(".") ? entry.substring(0, entry.lastIndexOf(".")) : entry;
|
|
42
|
+
if (entryNoExt.toLowerCase() === noExt.toLowerCase()) {
|
|
43
|
+
const fullPath = path.join(parentDir, entry);
|
|
44
|
+
const stat = fs.statSync(fullPath);
|
|
45
|
+
candidates.push({ type: stat.isDirectory() ? "dir" : "file", path: fullPath });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (candidates.length === 0) {
|
|
52
|
+
const root = cwd;
|
|
53
|
+
const rootEntries = fs.readdirSync(root);
|
|
54
|
+
const lowerBase = baseName.toLowerCase();
|
|
55
|
+
|
|
56
|
+
for (const entry of rootEntries) {
|
|
57
|
+
if (entry.toLowerCase() === lowerBase) {
|
|
58
|
+
const fullPath = path.join(root, entry);
|
|
59
|
+
const stat = fs.statSync(fullPath);
|
|
60
|
+
candidates.push({ type: stat.isDirectory() ? "dir" : "file", path: fullPath });
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (candidates.length === 0) {
|
|
66
|
+
const noExt2 = baseName.includes(".") ? null : baseName;
|
|
67
|
+
if (noExt2) {
|
|
68
|
+
for (const entry of rootEntries) {
|
|
69
|
+
const entryNoExt = entry.includes(".") ? entry.substring(0, entry.lastIndexOf(".")) : entry;
|
|
70
|
+
if (entryNoExt.toLowerCase() === noExt2.toLowerCase()) {
|
|
71
|
+
const fullPath = path.join(root, entry);
|
|
72
|
+
const stat = fs.statSync(fullPath);
|
|
73
|
+
candidates.push({ type: stat.isDirectory() ? "dir" : "file", path: fullPath });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return candidates;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function findInCwd(filename: string, cwd: string): string | null {
|
|
84
|
+
const entries = fs.readdirSync(cwd);
|
|
85
|
+
const lowerName = filename.toLowerCase();
|
|
86
|
+
|
|
87
|
+
for (const entry of entries) {
|
|
88
|
+
if (entry.toLowerCase() === lowerName) {
|
|
89
|
+
return path.join(cwd, entry);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const noExt = filename.includes(".") ? null : filename;
|
|
94
|
+
if (noExt) {
|
|
95
|
+
for (const entry of entries) {
|
|
96
|
+
const entryNoExt = entry.includes(".") ? entry.substring(0, entry.lastIndexOf(".")) : entry;
|
|
97
|
+
if (entryNoExt.toLowerCase() === noExt.toLowerCase()) {
|
|
98
|
+
return path.join(cwd, entry);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CortexFlow benchmark workflow — multi-step mail triage.
|
|
3
|
+
*
|
|
4
|
+
* LLM call budget (fixed regardless of inbox size):
|
|
5
|
+
* #1 Intent classification (CortexFlowOrchestrator)
|
|
6
|
+
* #2 Batch urgency check (one call for all N mails)
|
|
7
|
+
* #3 Batch response draft (one call for all urgent mails; skipped if none)
|
|
8
|
+
*/
|
|
9
|
+
import { TransitionAction } from '../routing/index';
|
|
10
|
+
import { CortexFlowOrchestrator } from '../routing/orchestrator';
|
|
11
|
+
import { ToolRegistry } from '../execution/registry';
|
|
12
|
+
import { GraphFlow } from '../execution/index';
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
import { HybridIntentClassifier } from '../routing/intent-classifier';
|
|
15
|
+
import { google } from 'googleapis';
|
|
16
|
+
import * as fs from 'fs';
|
|
17
|
+
import * as path from 'path';
|
|
18
|
+
import { LLMCall, safeParse } from './llm-client';
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Gmail helper
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
function createGmailClient() {
|
|
25
|
+
const credentials = JSON.parse(fs.readFileSync(path.join(__dirname, '../client_secret.json'), 'utf8'));
|
|
26
|
+
const token = JSON.parse(fs.readFileSync(path.join(__dirname, '../gmail_token.json'), 'utf8'));
|
|
27
|
+
const oauth2Client = new google.auth.OAuth2(
|
|
28
|
+
credentials.web.client_id,
|
|
29
|
+
credentials.web.client_secret,
|
|
30
|
+
'http://localhost:3000/oauth2callback'
|
|
31
|
+
);
|
|
32
|
+
oauth2Client.setCredentials(token);
|
|
33
|
+
return google.gmail({ version: 'v1', auth: oauth2Client });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Export
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
export async function runCortexFlowBenchmark(llm: LLMCall) {
|
|
41
|
+
console.log('\n🚀 CortexFlow benchmark...\n');
|
|
42
|
+
llm.resetCount();
|
|
43
|
+
|
|
44
|
+
const startTime = Date.now();
|
|
45
|
+
const startMemory = process.memoryUsage().heapUsed;
|
|
46
|
+
|
|
47
|
+
const gmail = createGmailClient();
|
|
48
|
+
const toolRegistry = new ToolRegistry();
|
|
49
|
+
const orchestrator = new CortexFlowOrchestrator('mail_triage', toolRegistry);
|
|
50
|
+
|
|
51
|
+
// Intent classification — keyword rules first, LLM fallback for ambiguous messages.
|
|
52
|
+
// The benchmark input contains "fetch", "mail", "urgent", "archive" → keyword match,
|
|
53
|
+
// confidence 0.95, no LLM call needed for classification.
|
|
54
|
+
const classifier = new HybridIntentClassifier(
|
|
55
|
+
[
|
|
56
|
+
{ intent: 'TRIAGE_MAILS', keywords: ['fetch', 'mail', 'urgent', 'archive'], confidence: 0.95 },
|
|
57
|
+
{ intent: 'TRIAGE_MAILS', keywords: ['triage', 'mail'], confidence: 0.95 },
|
|
58
|
+
{ intent: 'FETCH_MAILS', keywords: ['fetch', 'email'], confidence: 0.90 },
|
|
59
|
+
{ intent: 'FETCH_MAILS', keywords: ['fetch', 'mail'], confidence: 0.90 },
|
|
60
|
+
],
|
|
61
|
+
{ intents: ['TRIAGE_MAILS', 'FETCH_MAILS', 'UNKNOWN'], confidenceThreshold: 0.6 },
|
|
62
|
+
llm.call.bind(llm), // LLM fallback for messages that don't match any rule
|
|
63
|
+
);
|
|
64
|
+
orchestrator.setIntentClassifier(HybridIntentClassifier.toFn(classifier), classifier);
|
|
65
|
+
orchestrator.setLLMCall(llm.call.bind(llm));
|
|
66
|
+
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Petri Net: idle ──[process_mails]──► processing ──[complete]──► done
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
const net = orchestrator.petri;
|
|
72
|
+
net.addPlace({ id: 'idle', type: 'initial', tokens: [{ id: 'start', data: {}, createdAt: 0 }] });
|
|
73
|
+
net.addPlace({ id: 'processing', type: 'normal', tokens: [] });
|
|
74
|
+
net.addPlace({ id: 'done', type: 'final', tokens: [] });
|
|
75
|
+
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// GraphFlow — 4 nodes, 2 LLM calls
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
const mailGraph = new GraphFlow<any>({
|
|
81
|
+
name: 'process_mails',
|
|
82
|
+
context: { maxMails: 5 },
|
|
83
|
+
schema: z.object({ maxMails: z.number() }).passthrough(),
|
|
84
|
+
nodes: [
|
|
85
|
+
{
|
|
86
|
+
name: 'fetch_mails',
|
|
87
|
+
execute: async (ctx: any) => {
|
|
88
|
+
console.log(' [CortexFlow] Fetching mails...');
|
|
89
|
+
const res = await gmail.users.messages.list({ userId: 'me', maxResults: ctx.maxMails || 5, q: '' });
|
|
90
|
+
const messages = res.data.messages || [];
|
|
91
|
+
ctx.mails = [];
|
|
92
|
+
for (const msg of messages) {
|
|
93
|
+
const detail = await gmail.users.messages.get({ userId: 'me', id: msg.id!, format: 'metadata', metadataHeaders: ['Subject', 'From'] });
|
|
94
|
+
const headers = detail.data.payload?.headers || [];
|
|
95
|
+
ctx.mails.push({
|
|
96
|
+
id: msg.id!,
|
|
97
|
+
subject: headers.find((h: any) => h.name === 'Subject')?.value || '(no subject)',
|
|
98
|
+
from: headers.find((h: any) => h.name === 'From')?.value || '(unknown)',
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
console.log(` [CortexFlow] Fetched ${ctx.mails.length} mails`);
|
|
102
|
+
},
|
|
103
|
+
next: 'classify_urgency',
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'classify_urgency',
|
|
107
|
+
execute: async (ctx: any) => {
|
|
108
|
+
console.log(' [CortexFlow] Batch urgency (LLM call #2)...');
|
|
109
|
+
const mailList = ctx.mails.map((m: any, i: number) => `[${i}] From: ${m.from} | Subject: ${m.subject}`).join('\n');
|
|
110
|
+
const raw = await llm.call(
|
|
111
|
+
`For each email, is it urgent (needs reply today)? Urgent = deadline/ASAP/security alert/action required.\n\nEmails:\n${mailList}\n\nJSON: {"urgency":[{"index":0,"urgent":true},...]}`
|
|
112
|
+
);
|
|
113
|
+
const parsed = safeParse<any>(raw, { urgency: [] });
|
|
114
|
+
const results: any[] = Array.isArray(parsed.urgency) ? parsed.urgency : [];
|
|
115
|
+
ctx.urgentMails = ctx.mails.filter((_: any, i: number) => results.find((r: any) => r.index === i)?.urgent === true);
|
|
116
|
+
ctx.nonUrgentMails = ctx.mails.filter((_: any, i: number) => !results.find((r: any) => r.index === i)?.urgent);
|
|
117
|
+
console.log(` [CortexFlow] Urgent: ${ctx.urgentMails.length}, Non-urgent: ${ctx.nonUrgentMails.length}`);
|
|
118
|
+
},
|
|
119
|
+
next: 'respond_urgent',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: 'respond_urgent',
|
|
123
|
+
execute: async (ctx: any) => {
|
|
124
|
+
if (!ctx.urgentMails?.length) {
|
|
125
|
+
ctx.responses = [];
|
|
126
|
+
console.log(' [CortexFlow] No urgent mails — response generation skipped');
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
console.log(` [CortexFlow] Batch response draft for ${ctx.urgentMails.length} mail(s) (LLM call #3)...`);
|
|
130
|
+
const mailList = ctx.urgentMails.map((m: any, i: number) => `[${i}] From: ${m.from} | Subject: ${m.subject}`).join('\n');
|
|
131
|
+
const raw = await llm.call(
|
|
132
|
+
`Draft a brief professional reply (<40 words) for each urgent email.\n\nEmails:\n${mailList}\n\nJSON: {"responses":[{"index":0,"response":"..."},...]}`
|
|
133
|
+
);
|
|
134
|
+
const parsed = safeParse<any>(raw, { responses: [] });
|
|
135
|
+
ctx.responses = Array.isArray(parsed.responses) ? parsed.responses : [];
|
|
136
|
+
console.log(` [CortexFlow] Drafted ${ctx.responses.length} response(s)`);
|
|
137
|
+
},
|
|
138
|
+
next: 'archive_others',
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'archive_others',
|
|
142
|
+
execute: async (ctx: any) => {
|
|
143
|
+
ctx.archivedCount = ctx.nonUrgentMails?.length || 0;
|
|
144
|
+
console.log(` [CortexFlow] Archive: ${ctx.archivedCount} mail(s) (no LLM)`);
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
entryNode: 'fetch_mails',
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
toolRegistry.register({
|
|
152
|
+
name: 'process_mails',
|
|
153
|
+
description: 'Fetch, triage, respond to urgent mails and archive the rest',
|
|
154
|
+
graph: mailGraph,
|
|
155
|
+
startNode: 'fetch_mails',
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
net.addTransition({
|
|
159
|
+
id: 'process_mails', from: ['idle'], to: 'processing',
|
|
160
|
+
action: { type: 'graphflow', name: 'process_mails', contextMapper: (ctx) => ({ maxMails: ctx.maxMails || 5 }) } as TransitionAction,
|
|
161
|
+
});
|
|
162
|
+
net.addTransition({ id: 'complete', from: ['processing'], to: 'done' });
|
|
163
|
+
|
|
164
|
+
const sessionId = orchestrator.startSession();
|
|
165
|
+
const result = await orchestrator.orchestrate(
|
|
166
|
+
'Fetch my 5 latest mails, flag the urgent ones, draft replies for urgent mails, and archive the rest.',
|
|
167
|
+
sessionId
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const ar = (result as any).transitionResult?.actionResult ?? {};
|
|
171
|
+
return {
|
|
172
|
+
framework: 'CortexFlow',
|
|
173
|
+
totalTime: Date.now() - startTime,
|
|
174
|
+
llmCalls: llm.callCount,
|
|
175
|
+
memoryUsed: process.memoryUsage().heapUsed - startMemory,
|
|
176
|
+
mailsCount: ar.mails?.length ?? 0,
|
|
177
|
+
urgentCount: ar.urgentMails?.length ?? 0,
|
|
178
|
+
responsesCount: ar.responses?.length ?? 0,
|
|
179
|
+
archivedCount: ar.archivedCount ?? 0,
|
|
180
|
+
intentConfidence: result.intent.confidence,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LangGraph benchmark workflows — multi-step mail triage.
|
|
3
|
+
*
|
|
4
|
+
* Two variants:
|
|
5
|
+
*
|
|
6
|
+
* runLangGraphNaiveBenchmark — one LLM call per urgency decision (N mails = N calls).
|
|
7
|
+
* Mirrors the standard LangGraph supervisor/router pattern.
|
|
8
|
+
* LLM calls: 1 intent + 5 urgency + 0–1 response = 6–7
|
|
9
|
+
*
|
|
10
|
+
* runLangGraphOptimisedBenchmark — urgency batched manually by the developer.
|
|
11
|
+
* LLM calls: 1 intent + 1 urgency batch + 0–1 response = 2–3
|
|
12
|
+
*/
|
|
13
|
+
import { StateGraph, Annotation } from '@langchain/langgraph';
|
|
14
|
+
import { google } from 'googleapis';
|
|
15
|
+
import * as fs from 'fs';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
import { LLMCall, safeParse } from './llm-client';
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// State definition
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
const MailState = Annotation.Root({
|
|
24
|
+
messages: Annotation<Array<{ role: 'user' | 'assistant'; content: string }>>({
|
|
25
|
+
reducer: (curr, update) => curr.concat(update),
|
|
26
|
+
}),
|
|
27
|
+
intent: Annotation<string>(),
|
|
28
|
+
mails: Annotation<any[]>(),
|
|
29
|
+
urgentMails: Annotation<any[]>(),
|
|
30
|
+
responses: Annotation<any[]>(),
|
|
31
|
+
archivedCount: Annotation<number>(),
|
|
32
|
+
maxMails: Annotation<number>(),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Gmail helper
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
async function fetchMails(maxResults = 5) {
|
|
40
|
+
console.log(' [LangGraph] Fetching mails...');
|
|
41
|
+
const credentials = JSON.parse(fs.readFileSync(path.join(__dirname, '../client_secret.json'), 'utf8'));
|
|
42
|
+
const token = JSON.parse(fs.readFileSync(path.join(__dirname, '../gmail_token.json'), 'utf8'));
|
|
43
|
+
const oauth2Client = new google.auth.OAuth2(
|
|
44
|
+
credentials.web.client_id,
|
|
45
|
+
credentials.web.client_secret,
|
|
46
|
+
'http://localhost:3000/oauth2callback'
|
|
47
|
+
);
|
|
48
|
+
oauth2Client.setCredentials(token);
|
|
49
|
+
const gmail = google.gmail({ version: 'v1', auth: oauth2Client });
|
|
50
|
+
|
|
51
|
+
const res = await gmail.users.messages.list({ userId: 'me', maxResults, q: '' });
|
|
52
|
+
const messages = res.data.messages || [];
|
|
53
|
+
const mails: any[] = [];
|
|
54
|
+
for (const msg of messages) {
|
|
55
|
+
const detail = await gmail.users.messages.get({ userId: 'me', id: msg.id!, format: 'metadata', metadataHeaders: ['Subject', 'From'] });
|
|
56
|
+
const headers = detail.data.payload?.headers || [];
|
|
57
|
+
mails.push({
|
|
58
|
+
id: msg.id,
|
|
59
|
+
subject: headers.find((h: any) => h.name === 'Subject')?.value || '(no subject)',
|
|
60
|
+
from: headers.find((h: any) => h.name === 'From')?.value || '(unknown)',
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return mails;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Node factories (capture llm via closure)
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
function makeNodes(llm: LLMCall, variant: 'naive' | 'optimised') {
|
|
71
|
+
async function classifyIntent(state: typeof MailState.State) {
|
|
72
|
+
console.log(` [LangGraph/${variant}] Classifying intent...`);
|
|
73
|
+
const last = state.messages[state.messages.length - 1]?.content || '';
|
|
74
|
+
const raw = await llm.call(
|
|
75
|
+
`Intent classifier. Intents: TRIAGE_MAILS, FETCH_MAILS, UNKNOWN.\nMessage: "${last}"\nJSON: {"intent":"INTENT","params":{"count":5}}`
|
|
76
|
+
);
|
|
77
|
+
const parsed = safeParse<any>(raw, { intent: 'TRIAGE_MAILS', params: { count: 5 } });
|
|
78
|
+
return { intent: parsed.intent, maxMails: parsed.params?.count || 5 };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function fetchMailsNode(state: typeof MailState.State) {
|
|
82
|
+
const mails = await fetchMails(state.maxMails || 5);
|
|
83
|
+
console.log(` [LangGraph/${variant}] Fetched ${mails.length} mails`);
|
|
84
|
+
return { mails };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Naive: one LLM call per mail. */
|
|
88
|
+
async function decideUrgencyNaive(state: typeof MailState.State) {
|
|
89
|
+
console.log(` [LangGraph/naive] Urgency check per mail (${state.mails.length} LLM calls)...`);
|
|
90
|
+
const urgentMails: any[] = [];
|
|
91
|
+
for (const mail of state.mails) {
|
|
92
|
+
const raw = await llm.call(
|
|
93
|
+
`Is this email urgent (needs reply today)? Urgent = security alert/deadline/ASAP/action required.\nFrom: ${mail.from}\nSubject: ${mail.subject}\nJSON: {"urgent":true}`
|
|
94
|
+
);
|
|
95
|
+
const parsed = safeParse<any>(raw, { urgent: false });
|
|
96
|
+
if (parsed.urgent === true) urgentMails.push(mail);
|
|
97
|
+
}
|
|
98
|
+
console.log(` [LangGraph/naive] Urgent: ${urgentMails.length}`);
|
|
99
|
+
return { urgentMails };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Optimised: one batch LLM call for all mails. */
|
|
103
|
+
async function decideUrgencyBatch(state: typeof MailState.State) {
|
|
104
|
+
console.log(' [LangGraph/optimised] Batch urgency (1 LLM call)...');
|
|
105
|
+
const mailList = state.mails.map((m: any, i: number) => `[${i}] From: ${m.from} | Subject: ${m.subject}`).join('\n');
|
|
106
|
+
const raw = await llm.call(
|
|
107
|
+
`For each email, is it urgent (needs reply today)?\n\nEmails:\n${mailList}\n\nJSON: {"urgency":[{"index":0,"urgent":true},...]}`
|
|
108
|
+
);
|
|
109
|
+
const parsed = safeParse<any>(raw, { urgency: [] });
|
|
110
|
+
const results = Array.isArray(parsed.urgency) ? parsed.urgency : [];
|
|
111
|
+
const urgentMails = state.mails.filter((_: any, i: number) => results.find((r: any) => r.index === i)?.urgent === true);
|
|
112
|
+
console.log(` [LangGraph/optimised] Urgent: ${urgentMails.length}`);
|
|
113
|
+
return { urgentMails };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function generateResponsesNode(state: typeof MailState.State) {
|
|
117
|
+
if (!state.urgentMails?.length) {
|
|
118
|
+
console.log(` [LangGraph/${variant}] No urgent mails — skipping`);
|
|
119
|
+
return { responses: [] };
|
|
120
|
+
}
|
|
121
|
+
console.log(` [LangGraph/${variant}] Batch response draft (1 LLM call)...`);
|
|
122
|
+
const mailList = state.urgentMails.map((m: any, i: number) => `[${i}] From: ${m.from} | Subject: ${m.subject}`).join('\n');
|
|
123
|
+
const raw = await llm.call(
|
|
124
|
+
`Draft a brief professional reply (<40 words) for each urgent email.\n\nEmails:\n${mailList}\n\nJSON: {"responses":[{"index":0,"response":"..."},...]}`
|
|
125
|
+
);
|
|
126
|
+
const parsed = safeParse<any>(raw, { responses: [] });
|
|
127
|
+
const responses = Array.isArray(parsed.responses) ? parsed.responses : [];
|
|
128
|
+
console.log(` [LangGraph/${variant}] Drafted ${responses.length} response(s)`);
|
|
129
|
+
return { responses };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function archiveNode(state: typeof MailState.State) {
|
|
133
|
+
const urgentIds = new Set((state.urgentMails || []).map((m: any) => m.id));
|
|
134
|
+
const count = (state.mails || []).filter((m: any) => !urgentIds.has(m.id)).length;
|
|
135
|
+
console.log(` [LangGraph/${variant}] Archive ${count} mail(s) (no LLM)`);
|
|
136
|
+
return { archivedCount: count };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return { classifyIntent, fetchMailsNode, decideUrgencyNaive, decideUrgencyBatch, generateResponsesNode, archiveNode };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// Graph builder
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
function buildGraph(llm: LLMCall, variant: 'naive' | 'optimised') {
|
|
147
|
+
const nodes = makeNodes(llm, variant);
|
|
148
|
+
const urgencyNode = variant === 'naive' ? nodes.decideUrgencyNaive : nodes.decideUrgencyBatch;
|
|
149
|
+
|
|
150
|
+
return new StateGraph(MailState)
|
|
151
|
+
.addNode('classify', nodes.classifyIntent)
|
|
152
|
+
.addNode('fetch_mails', nodes.fetchMailsNode)
|
|
153
|
+
.addNode('decide_urgency', urgencyNode)
|
|
154
|
+
.addNode('generate_responses', nodes.generateResponsesNode)
|
|
155
|
+
.addNode('archive', nodes.archiveNode)
|
|
156
|
+
.addEdge('__start__', 'classify')
|
|
157
|
+
.addEdge('classify', 'fetch_mails')
|
|
158
|
+
.addEdge('fetch_mails', 'decide_urgency')
|
|
159
|
+
.addEdge('decide_urgency', 'generate_responses')
|
|
160
|
+
.addEdge('generate_responses', 'archive')
|
|
161
|
+
.addEdge('archive', '__end__')
|
|
162
|
+
.compile();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ---------------------------------------------------------------------------
|
|
166
|
+
// Runner helper
|
|
167
|
+
// ---------------------------------------------------------------------------
|
|
168
|
+
|
|
169
|
+
const INPUT = {
|
|
170
|
+
messages: [{ role: 'user' as const, content: 'Fetch my 5 latest mails, flag the urgent ones, draft replies for urgent mails, and archive the rest.' }],
|
|
171
|
+
maxMails: 5,
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
async function run(label: string, llm: LLMCall, variant: 'naive' | 'optimised') {
|
|
175
|
+
console.log(`\n🚀 LangGraph (${variant}) benchmark...\n`);
|
|
176
|
+
llm.resetCount();
|
|
177
|
+
const t0 = Date.now();
|
|
178
|
+
const m0 = process.memoryUsage().heapUsed;
|
|
179
|
+
|
|
180
|
+
const graph = buildGraph(llm, variant);
|
|
181
|
+
const result = await graph.invoke(INPUT);
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
framework: label,
|
|
185
|
+
totalTime: Date.now() - t0,
|
|
186
|
+
llmCalls: llm.callCount,
|
|
187
|
+
memoryUsed: process.memoryUsage().heapUsed - m0,
|
|
188
|
+
mailsCount: result.mails?.length ?? 0,
|
|
189
|
+
urgentCount: result.urgentMails?.length ?? 0,
|
|
190
|
+
responsesCount: result.responses?.length ?? 0,
|
|
191
|
+
archivedCount: result.archivedCount ?? 0,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export async function runLangGraphNaiveBenchmark(llm: LLMCall) {
|
|
196
|
+
return run('LangGraph (naive)', llm, 'naive');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export async function runLangGraphOptimisedBenchmark(llm: LLMCall) {
|
|
200
|
+
return run('LangGraph (optimised)', llm, 'optimised');
|
|
201
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file llm-client.ts
|
|
3
|
+
* @description Thin OpenAI-compatible LLM client shared by all benchmark variants.
|
|
4
|
+
*
|
|
5
|
+
* Works with any OpenAI-compatible endpoint:
|
|
6
|
+
* - Ollama local : http://localhost:11434/v1 (no key required)
|
|
7
|
+
* - Groq : https://api.groq.com/openai/v1
|
|
8
|
+
* - OpenRouter : https://openrouter.ai/api/v1
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export interface LLMConfig {
|
|
12
|
+
baseUrl: string;
|
|
13
|
+
apiKey: string;
|
|
14
|
+
model: string;
|
|
15
|
+
/** Request timeout in ms. Default: 60 000 */
|
|
16
|
+
timeout?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface LLMCall {
|
|
20
|
+
/** Number of LLM calls made since creation (for benchmark accounting). */
|
|
21
|
+
callCount: number;
|
|
22
|
+
/** Send a prompt and return the text content of the first choice. */
|
|
23
|
+
call(prompt: string): Promise<string>;
|
|
24
|
+
/** Reset the call counter (used between benchmark variants). */
|
|
25
|
+
resetCount(): void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Creates an LLMCall instance backed by an OpenAI-compatible chat endpoint.
|
|
30
|
+
* The `format: json` instruction is embedded in the system prompt so that
|
|
31
|
+
* both providers that support the `response_format` field and those that do
|
|
32
|
+
* not (e.g. some Ollama models) behave consistently.
|
|
33
|
+
*/
|
|
34
|
+
export function createLLMClient(config: LLMConfig): LLMCall {
|
|
35
|
+
let callCount = 0;
|
|
36
|
+
|
|
37
|
+
async function call(prompt: string): Promise<string> {
|
|
38
|
+
callCount++;
|
|
39
|
+
const res = await fetch(`${config.baseUrl}/chat/completions`, {
|
|
40
|
+
method: 'POST',
|
|
41
|
+
headers: {
|
|
42
|
+
'Content-Type': 'application/json',
|
|
43
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
44
|
+
},
|
|
45
|
+
body: JSON.stringify({
|
|
46
|
+
model: config.model,
|
|
47
|
+
messages: [
|
|
48
|
+
{ role: 'system', content: 'You are a helpful assistant. Always respond with valid JSON.' },
|
|
49
|
+
{ role: 'user', content: prompt },
|
|
50
|
+
],
|
|
51
|
+
temperature: 0,
|
|
52
|
+
}),
|
|
53
|
+
signal: AbortSignal.timeout(config.timeout ?? 60_000),
|
|
54
|
+
});
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
const body = await res.text();
|
|
57
|
+
throw new Error(`LLM API error ${res.status}: ${body.slice(0, 200)}`);
|
|
58
|
+
}
|
|
59
|
+
const data = await res.json();
|
|
60
|
+
return data.choices?.[0]?.message?.content ?? '';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
get callCount() { return callCount; },
|
|
65
|
+
call,
|
|
66
|
+
resetCount() { callCount = 0; },
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Safely parse LLM JSON output; returns `fallback` on failure. */
|
|
71
|
+
export function safeParse<T>(raw: string, fallback: T): T {
|
|
72
|
+
// Strip markdown code fences if present
|
|
73
|
+
const cleaned = raw.replace(/^```(?:json)?\s*/i, '').replace(/\s*```\s*$/i, '').trim();
|
|
74
|
+
try { return JSON.parse(cleaned) as T; }
|
|
75
|
+
catch { return fallback; }
|
|
76
|
+
}
|