synapsekit 0.6.0__tar.gz → 0.6.2__tar.gz
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.
- {synapsekit-0.6.0 → synapsekit-0.6.2}/CHANGELOG.md +43 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/PKG-INFO +10 -2
- {synapsekit-0.6.0 → synapsekit-0.6.2}/pyproject.toml +17 -3
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/__init__.py +33 -1
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/agents/__init__.py +4 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/agents/tools/__init__.py +4 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/agents/tools/http_request.py +1 -3
- synapsekit-0.6.2/src/synapsekit/agents/tools/human_input.py +63 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/agents/tools/regex_tool.py +3 -1
- synapsekit-0.6.2/src/synapsekit/agents/tools/wikipedia.py +103 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/graph/__init__.py +7 -1
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/graph/compiled.py +77 -2
- synapsekit-0.6.2/src/synapsekit/graph/interrupt.py +53 -0
- synapsekit-0.6.2/src/synapsekit/graph/node.py +70 -0
- synapsekit-0.6.2/src/synapsekit/graph/subgraph.py +60 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/llm/_sqlite_cache.py +2 -5
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/llm/groq.py +1 -3
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/llm/structured.py +1 -1
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/loaders/excel.py +1 -3
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/loaders/pptx.py +1 -3
- synapsekit-0.6.2/src/synapsekit/memory/__init__.py +11 -0
- synapsekit-0.6.2/src/synapsekit/memory/hybrid.py +115 -0
- synapsekit-0.6.2/src/synapsekit/memory/sqlite.py +119 -0
- synapsekit-0.6.2/src/synapsekit/memory/summary_buffer.py +118 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/retrieval/__init__.py +14 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/retrieval/contextual.py +1 -3
- synapsekit-0.6.2/src/synapsekit/retrieval/contextual_compression.py +68 -0
- synapsekit-0.6.2/src/synapsekit/retrieval/crag.py +123 -0
- synapsekit-0.6.2/src/synapsekit/retrieval/cross_encoder.py +105 -0
- synapsekit-0.6.2/src/synapsekit/retrieval/ensemble.py +50 -0
- synapsekit-0.6.2/src/synapsekit/retrieval/parent_document.py +104 -0
- synapsekit-0.6.2/src/synapsekit/retrieval/query_decomposition.py +87 -0
- synapsekit-0.6.2/src/synapsekit/retrieval/self_query.py +98 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/retrieval/sentence_window.py +7 -5
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/test_v053_features.py +1 -3
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/test_v060_features.py +10 -5
- synapsekit-0.6.2/tests/test_v061_features.py +633 -0
- synapsekit-0.6.2/tests/test_v062_features.py +531 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/uv.lock +360 -2
- synapsekit-0.6.0/src/synapsekit/graph/node.py +0 -34
- synapsekit-0.6.0/src/synapsekit/memory/__init__.py +0 -3
- {synapsekit-0.6.0 → synapsekit-0.6.2}/.github/DISCUSSION_TEMPLATE/ideas.yml +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/.github/profile/README.md +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/.github/workflows/ci.yml +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/.gitignore +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/.pre-commit-config.yaml +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/CODE_OF_CONDUCT.md +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/CONTRIBUTING.md +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/LICENSE +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/Makefile +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/README.md +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/SECURITY.md +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/assets/banner.svg +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/assets/favicon.svg +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/assets/logo.svg +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/_compat.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/agents/base.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/agents/executor.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/agents/function_calling.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/agents/memory.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/agents/react.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/agents/registry.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/agents/tool_decorator.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/agents/tools/calculator.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/agents/tools/datetime_tool.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/agents/tools/file_list.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/agents/tools/file_read.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/agents/tools/file_write.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/agents/tools/json_query.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/agents/tools/python_repl.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/agents/tools/sql_query.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/agents/tools/web_search.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/embeddings/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/embeddings/backend.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/graph/checkpointers/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/graph/checkpointers/base.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/graph/checkpointers/memory.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/graph/checkpointers/sqlite.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/graph/edge.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/graph/errors.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/graph/graph.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/graph/mermaid.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/graph/state.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/llm/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/llm/_cache.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/llm/_rate_limit.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/llm/_retry.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/llm/anthropic.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/llm/azure_openai.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/llm/base.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/llm/bedrock.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/llm/cohere.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/llm/deepseek.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/llm/fireworks.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/llm/gemini.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/llm/mistral.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/llm/ollama.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/llm/openai.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/llm/openrouter.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/llm/together.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/loaders/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/loaders/base.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/loaders/csv.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/loaders/directory.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/loaders/html.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/loaders/json_loader.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/loaders/pdf.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/loaders/text.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/loaders/web.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/memory/conversation.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/observability/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/observability/tracer.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/parsers/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/parsers/json_parser.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/parsers/list_parser.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/parsers/pydantic_parser.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/prompts/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/prompts/template.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/py.typed +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/rag/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/rag/facade.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/rag/pipeline.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/retrieval/base.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/retrieval/chroma.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/retrieval/faiss.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/retrieval/pinecone.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/retrieval/qdrant.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/retrieval/rag_fusion.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/retrieval/retriever.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/retrieval/vectorstore.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/text_splitters/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/text_splitters/base.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/text_splitters/character.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/text_splitters/recursive.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/text_splitters/semantic.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/src/synapsekit/text_splitters/token.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/agents/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/agents/test_executor.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/agents/test_function_calling.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/agents/test_memory.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/agents/test_react.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/agents/test_tool_decorator.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/agents/test_tools.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/conftest.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/graph/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/graph/test_build.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/graph/test_checkpointing.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/graph/test_cycles.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/graph/test_mermaid.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/graph/test_run.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/graph/test_state.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/graph/test_stream.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/llm/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/llm/test_cache_retry.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/llm/test_function_calling_providers.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/llm/test_llm.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/llm/test_providers.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/loaders/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/loaders/test_loaders.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/memory/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/memory/test_memory.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/observability/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/observability/test_tracer.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/parsers/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/parsers/test_parsers.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/prompts/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/prompts/test_prompts.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/rag/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/rag/test_facade.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/rag/test_pipeline.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/retrieval/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/retrieval/test_backends.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/retrieval/test_retriever.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/retrieval/test_vectorstore.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/test_v051_features.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/test_v052_features.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/text_splitters/__init__.py +0 -0
- {synapsekit-0.6.0 → synapsekit-0.6.2}/tests/text_splitters/test_splitters.py +0 -0
|
@@ -7,6 +7,49 @@ SynapseKit uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.6.1] — 2026-03-13
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Human-in-the-Loop** — `GraphInterrupt` exception pauses graph execution for human review; `InterruptState` holds interrupt details; `resume(updates=...)` applies human edits and continues from checkpoint
|
|
15
|
+
- **Subgraphs** — `subgraph_node(compiled_graph, input_mapping, output_mapping)` nests a `CompiledGraph` as a node in a parent graph with key mapping
|
|
16
|
+
- **Token-level streaming** — `llm_node(llm, stream=True)` wraps any `BaseLLM` as a graph node; `stream_tokens()` yields `{"type": "token", ...}` events for real-time output
|
|
17
|
+
- **Self-Query retrieval** — `SelfQueryRetriever` uses an LLM to decompose natural-language queries into semantic search + metadata filters automatically
|
|
18
|
+
- **Parent Document retrieval** — `ParentDocumentRetriever` embeds small chunks for precision search, returns full parent documents for richer context
|
|
19
|
+
- **Cross-Encoder reranking** — `CrossEncoderReranker` reranks retrieval results with cross-encoder models for higher accuracy (requires `synapsekit[semantic]`)
|
|
20
|
+
- **Hybrid memory** — `HybridMemory` keeps a sliding window of recent messages in full, plus an LLM-generated summary of older messages for token-efficient long conversations
|
|
21
|
+
- 30 new tests (482 total)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## [0.6.0] — 2026-03-13
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- **Built-in tools** (6 new):
|
|
30
|
+
- `HTTPRequestTool` — GET/POST/PUT/DELETE/PATCH with aiohttp, configurable timeout and max response length
|
|
31
|
+
- `FileWriteTool` — write/append files with auto-mkdir
|
|
32
|
+
- `FileListTool` — list directories with glob patterns, recursive mode
|
|
33
|
+
- `DateTimeTool` — current time, parse, format with timezone support
|
|
34
|
+
- `RegexTool` — findall, match, search, replace, split with flag support
|
|
35
|
+
- `JSONQueryTool` — dot-notation path queries on JSON data
|
|
36
|
+
- **LLM providers** (3 new, all OpenAI-compatible):
|
|
37
|
+
- `OpenRouterLLM` — unified API for 200+ models (auto-detected from `/` in model name)
|
|
38
|
+
- `TogetherLLM` — Together AI fast inference
|
|
39
|
+
- `FireworksLLM` — Fireworks AI optimized serving
|
|
40
|
+
- **Advanced retrieval** (2 new):
|
|
41
|
+
- `ContextualRetriever` — Anthropic-style contextual retrieval (LLM prepends context before embedding)
|
|
42
|
+
- `SentenceWindowRetriever` — sentence-level embedding with configurable window expansion at retrieval time
|
|
43
|
+
- RAG facade auto-detects `openrouter` (model names with `/`), `together`, and `fireworks` providers
|
|
44
|
+
- 37 new tests (452 total)
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
|
|
48
|
+
- Lazy imports extended for new providers (`OpenRouterLLM`, `TogetherLLM`, `FireworksLLM`)
|
|
49
|
+
- `agents/tools/__init__.py` exports 11 built-in tools (was 5)
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
10
53
|
## [0.5.3] — 2026-03-12
|
|
11
54
|
|
|
12
55
|
### Added
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: synapsekit
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: Async-native Python framework for building production-grade LLM applications. Streaming-first, 2 dependencies, fully transparent.
|
|
5
5
|
Project-URL: Homepage, https://github.com/SynapseKit/SynapseKit
|
|
6
6
|
Project-URL: Repository, https://github.com/SynapseKit/SynapseKit
|
|
7
|
-
Author-email: Amit
|
|
7
|
+
Author-email: Amit <de.amit.nautiyal@gmail.com>
|
|
8
8
|
License: MIT
|
|
9
9
|
License-File: LICENSE
|
|
10
10
|
Keywords: ai,async,llm,rag,retrieval,streaming
|
|
@@ -43,13 +43,19 @@ Provides-Extra: chroma
|
|
|
43
43
|
Requires-Dist: chromadb>=0.5; extra == 'chroma'
|
|
44
44
|
Provides-Extra: cohere
|
|
45
45
|
Requires-Dist: cohere>=5.0; extra == 'cohere'
|
|
46
|
+
Provides-Extra: excel
|
|
47
|
+
Requires-Dist: openpyxl>=3.1; extra == 'excel'
|
|
46
48
|
Provides-Extra: faiss
|
|
47
49
|
Requires-Dist: faiss-cpu>=1.7; extra == 'faiss'
|
|
48
50
|
Provides-Extra: gemini
|
|
49
51
|
Requires-Dist: google-generativeai>=0.7; extra == 'gemini'
|
|
52
|
+
Provides-Extra: groq
|
|
53
|
+
Requires-Dist: groq>=0.9; extra == 'groq'
|
|
50
54
|
Provides-Extra: html
|
|
51
55
|
Requires-Dist: beautifulsoup4>=4.12; extra == 'html'
|
|
52
56
|
Requires-Dist: lxml>=5.0; extra == 'html'
|
|
57
|
+
Provides-Extra: http
|
|
58
|
+
Requires-Dist: aiohttp>=3.9; extra == 'http'
|
|
53
59
|
Provides-Extra: mistral
|
|
54
60
|
Requires-Dist: mistralai>=1.0; extra == 'mistral'
|
|
55
61
|
Provides-Extra: ollama
|
|
@@ -60,6 +66,8 @@ Provides-Extra: pdf
|
|
|
60
66
|
Requires-Dist: pypdf>=4.0; extra == 'pdf'
|
|
61
67
|
Provides-Extra: pinecone
|
|
62
68
|
Requires-Dist: pinecone>=3.0; extra == 'pinecone'
|
|
69
|
+
Provides-Extra: pptx
|
|
70
|
+
Requires-Dist: python-pptx>=0.6; extra == 'pptx'
|
|
63
71
|
Provides-Extra: qdrant
|
|
64
72
|
Requires-Dist: qdrant-client>=1.9; extra == 'qdrant'
|
|
65
73
|
Provides-Extra: search
|
|
@@ -4,9 +4,9 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "synapsekit"
|
|
7
|
-
version = "0.6.
|
|
7
|
+
version = "0.6.2"
|
|
8
8
|
description = "Async-native Python framework for building production-grade LLM applications. Streaming-first, 2 dependencies, fully transparent."
|
|
9
|
-
authors = [{ name = "Amit
|
|
9
|
+
authors = [{ name = "Amit", email = "de.amit.nautiyal@gmail.com" }]
|
|
10
10
|
license = { text = "MIT" }
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
requires-python = ">=3.14"
|
|
@@ -44,6 +44,10 @@ cohere = ["cohere>=5.0"]
|
|
|
44
44
|
mistral = ["mistralai>=1.0"]
|
|
45
45
|
gemini = ["google-generativeai>=0.7"]
|
|
46
46
|
bedrock = ["boto3>=1.34"]
|
|
47
|
+
groq = ["groq>=0.9"]
|
|
48
|
+
excel = ["openpyxl>=3.1"]
|
|
49
|
+
pptx = ["python-pptx>=0.6"]
|
|
50
|
+
http = ["aiohttp>=3.9"]
|
|
47
51
|
search = ["duckduckgo-search>=6.0"]
|
|
48
52
|
all = [
|
|
49
53
|
"openai>=1.0",
|
|
@@ -130,6 +134,14 @@ module = [
|
|
|
130
134
|
"synapsekit.retrieval.faiss",
|
|
131
135
|
"synapsekit.retrieval.qdrant",
|
|
132
136
|
"synapsekit.retrieval.pinecone",
|
|
137
|
+
"synapsekit.llm.groq",
|
|
138
|
+
"synapsekit.llm.azure_openai",
|
|
139
|
+
"synapsekit.llm.deepseek",
|
|
140
|
+
"synapsekit.llm.openrouter",
|
|
141
|
+
"synapsekit.llm.together",
|
|
142
|
+
"synapsekit.llm.fireworks",
|
|
143
|
+
"synapsekit.loaders.excel",
|
|
144
|
+
"synapsekit.loaders.pptx",
|
|
133
145
|
]
|
|
134
146
|
warn_return_any = false
|
|
135
147
|
# from __future__ import annotations causes mypy to misinterpret
|
|
@@ -143,7 +155,9 @@ pep621_dev_dependency_groups = ["dev"]
|
|
|
143
155
|
google-generativeai = "google"
|
|
144
156
|
beautifulsoup4 = "bs4"
|
|
145
157
|
faiss-cpu = "faiss"
|
|
158
|
+
python-pptx = "pptx"
|
|
146
159
|
|
|
147
160
|
[tool.deptry.per_rule_ignores]
|
|
148
|
-
DEP001 = ["sqlalchemy", "pydantic"]
|
|
161
|
+
DEP001 = ["sqlalchemy", "pydantic", "aiohttp"]
|
|
149
162
|
DEP002 = ["numpy", "rank-bm25", "beautifulsoup4", "lxml", "faiss-cpu", "google-generativeai"]
|
|
163
|
+
DEP004 = ["pydantic"]
|
|
@@ -27,6 +27,7 @@ from .agents import (
|
|
|
27
27
|
FileWriteTool,
|
|
28
28
|
FunctionCallingAgent,
|
|
29
29
|
HTTPRequestTool,
|
|
30
|
+
HumanInputTool,
|
|
30
31
|
JSONQueryTool,
|
|
31
32
|
PythonREPLTool,
|
|
32
33
|
ReActAgent,
|
|
@@ -35,6 +36,7 @@ from .agents import (
|
|
|
35
36
|
ToolRegistry,
|
|
36
37
|
ToolResult,
|
|
37
38
|
WebSearchTool,
|
|
39
|
+
WikipediaTool,
|
|
38
40
|
tool,
|
|
39
41
|
)
|
|
40
42
|
from .embeddings.backend import SynapsekitEmbeddings
|
|
@@ -46,15 +48,19 @@ from .graph import (
|
|
|
46
48
|
ConditionFn,
|
|
47
49
|
Edge,
|
|
48
50
|
GraphConfigError,
|
|
51
|
+
GraphInterrupt,
|
|
49
52
|
GraphRuntimeError,
|
|
50
53
|
GraphState,
|
|
51
54
|
InMemoryCheckpointer,
|
|
55
|
+
InterruptState,
|
|
52
56
|
Node,
|
|
53
57
|
NodeFn,
|
|
54
58
|
SQLiteCheckpointer,
|
|
55
59
|
StateGraph,
|
|
56
60
|
agent_node,
|
|
61
|
+
llm_node,
|
|
57
62
|
rag_node,
|
|
63
|
+
subgraph_node,
|
|
58
64
|
)
|
|
59
65
|
from .llm.base import BaseLLM, LLMConfig
|
|
60
66
|
from .llm.structured import generate_structured
|
|
@@ -67,6 +73,9 @@ from .loaders.pdf import PDFLoader
|
|
|
67
73
|
from .loaders.text import StringLoader, TextLoader
|
|
68
74
|
from .loaders.web import WebLoader
|
|
69
75
|
from .memory.conversation import ConversationMemory
|
|
76
|
+
from .memory.hybrid import HybridMemory
|
|
77
|
+
from .memory.sqlite import SQLiteConversationMemory
|
|
78
|
+
from .memory.summary_buffer import SummaryBufferMemory
|
|
70
79
|
from .observability.tracer import TokenTracer
|
|
71
80
|
from .parsers.json_parser import JSONParser
|
|
72
81
|
from .parsers.list_parser import ListParser
|
|
@@ -76,8 +85,15 @@ from .rag.facade import RAG
|
|
|
76
85
|
from .rag.pipeline import RAGConfig, RAGPipeline
|
|
77
86
|
from .retrieval.base import VectorStore
|
|
78
87
|
from .retrieval.contextual import ContextualRetriever
|
|
88
|
+
from .retrieval.contextual_compression import ContextualCompressionRetriever
|
|
89
|
+
from .retrieval.crag import CRAGRetriever
|
|
90
|
+
from .retrieval.cross_encoder import CrossEncoderReranker
|
|
91
|
+
from .retrieval.ensemble import EnsembleRetriever
|
|
92
|
+
from .retrieval.parent_document import ParentDocumentRetriever
|
|
93
|
+
from .retrieval.query_decomposition import QueryDecompositionRetriever
|
|
79
94
|
from .retrieval.rag_fusion import RAGFusionRetriever
|
|
80
95
|
from .retrieval.retriever import Retriever
|
|
96
|
+
from .retrieval.self_query import SelfQueryRetriever
|
|
81
97
|
from .retrieval.sentence_window import SentenceWindowRetriever
|
|
82
98
|
from .retrieval.vectorstore import InMemoryVectorStore
|
|
83
99
|
from .text_splitters import (
|
|
@@ -88,7 +104,7 @@ from .text_splitters import (
|
|
|
88
104
|
TokenAwareSplitter,
|
|
89
105
|
)
|
|
90
106
|
|
|
91
|
-
__version__ = "0.6.
|
|
107
|
+
__version__ = "0.6.2"
|
|
92
108
|
__all__ = [
|
|
93
109
|
# Facade
|
|
94
110
|
"RAG",
|
|
@@ -117,9 +133,19 @@ __all__ = [
|
|
|
117
133
|
"Retriever",
|
|
118
134
|
"RAGFusionRetriever",
|
|
119
135
|
"ContextualRetriever",
|
|
136
|
+
"ContextualCompressionRetriever",
|
|
137
|
+
"CRAGRetriever",
|
|
138
|
+
"CrossEncoderReranker",
|
|
139
|
+
"EnsembleRetriever",
|
|
140
|
+
"ParentDocumentRetriever",
|
|
141
|
+
"QueryDecompositionRetriever",
|
|
142
|
+
"SelfQueryRetriever",
|
|
120
143
|
"SentenceWindowRetriever",
|
|
121
144
|
# Memory / observability
|
|
122
145
|
"ConversationMemory",
|
|
146
|
+
"HybridMemory",
|
|
147
|
+
"SQLiteConversationMemory",
|
|
148
|
+
"SummaryBufferMemory",
|
|
123
149
|
"TokenTracer",
|
|
124
150
|
# Loaders
|
|
125
151
|
"Document",
|
|
@@ -160,11 +186,13 @@ __all__ = [
|
|
|
160
186
|
"FileReadTool",
|
|
161
187
|
"FileWriteTool",
|
|
162
188
|
"HTTPRequestTool",
|
|
189
|
+
"HumanInputTool",
|
|
163
190
|
"JSONQueryTool",
|
|
164
191
|
"PythonREPLTool",
|
|
165
192
|
"RegexTool",
|
|
166
193
|
"SQLQueryTool",
|
|
167
194
|
"WebSearchTool",
|
|
195
|
+
"WikipediaTool",
|
|
168
196
|
# Text splitters
|
|
169
197
|
"BaseSplitter",
|
|
170
198
|
"CharacterTextSplitter",
|
|
@@ -179,7 +207,11 @@ __all__ = [
|
|
|
179
207
|
"Node",
|
|
180
208
|
"NodeFn",
|
|
181
209
|
"agent_node",
|
|
210
|
+
"llm_node",
|
|
182
211
|
"rag_node",
|
|
212
|
+
"subgraph_node",
|
|
213
|
+
"GraphInterrupt",
|
|
214
|
+
"InterruptState",
|
|
183
215
|
"Edge",
|
|
184
216
|
"ConditionalEdge",
|
|
185
217
|
"ConditionFn",
|
|
@@ -12,11 +12,13 @@ from .tools import (
|
|
|
12
12
|
FileReadTool,
|
|
13
13
|
FileWriteTool,
|
|
14
14
|
HTTPRequestTool,
|
|
15
|
+
HumanInputTool,
|
|
15
16
|
JSONQueryTool,
|
|
16
17
|
PythonREPLTool,
|
|
17
18
|
RegexTool,
|
|
18
19
|
SQLQueryTool,
|
|
19
20
|
WebSearchTool,
|
|
21
|
+
WikipediaTool,
|
|
20
22
|
)
|
|
21
23
|
|
|
22
24
|
__all__ = [
|
|
@@ -40,9 +42,11 @@ __all__ = [
|
|
|
40
42
|
"FileReadTool",
|
|
41
43
|
"FileWriteTool",
|
|
42
44
|
"HTTPRequestTool",
|
|
45
|
+
"HumanInputTool",
|
|
43
46
|
"JSONQueryTool",
|
|
44
47
|
"PythonREPLTool",
|
|
45
48
|
"RegexTool",
|
|
46
49
|
"SQLQueryTool",
|
|
47
50
|
"WebSearchTool",
|
|
51
|
+
"WikipediaTool",
|
|
48
52
|
]
|
|
@@ -4,11 +4,13 @@ from .file_list import FileListTool
|
|
|
4
4
|
from .file_read import FileReadTool
|
|
5
5
|
from .file_write import FileWriteTool
|
|
6
6
|
from .http_request import HTTPRequestTool
|
|
7
|
+
from .human_input import HumanInputTool
|
|
7
8
|
from .json_query import JSONQueryTool
|
|
8
9
|
from .python_repl import PythonREPLTool
|
|
9
10
|
from .regex_tool import RegexTool
|
|
10
11
|
from .sql_query import SQLQueryTool
|
|
11
12
|
from .web_search import WebSearchTool
|
|
13
|
+
from .wikipedia import WikipediaTool
|
|
12
14
|
|
|
13
15
|
__all__ = [
|
|
14
16
|
"CalculatorTool",
|
|
@@ -17,9 +19,11 @@ __all__ = [
|
|
|
17
19
|
"FileReadTool",
|
|
18
20
|
"FileWriteTool",
|
|
19
21
|
"HTTPRequestTool",
|
|
22
|
+
"HumanInputTool",
|
|
20
23
|
"JSONQueryTool",
|
|
21
24
|
"PythonREPLTool",
|
|
22
25
|
"RegexTool",
|
|
23
26
|
"SQLQueryTool",
|
|
24
27
|
"WebSearchTool",
|
|
28
|
+
"WikipediaTool",
|
|
25
29
|
]
|
|
@@ -56,9 +56,7 @@ class HTTPRequestTool(BaseTool):
|
|
|
56
56
|
try:
|
|
57
57
|
import aiohttp
|
|
58
58
|
except ImportError:
|
|
59
|
-
raise ImportError(
|
|
60
|
-
"aiohttp required for HTTPRequestTool: pip install aiohttp"
|
|
61
|
-
) from None
|
|
59
|
+
raise ImportError("aiohttp required for HTTPRequestTool: pip install aiohttp") from None
|
|
62
60
|
|
|
63
61
|
method = method.upper()
|
|
64
62
|
req_headers = headers or {}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Human Input Tool: allows agents to ask the user a question mid-execution."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from ..base import BaseTool, ToolResult
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class HumanInputTool(BaseTool):
|
|
12
|
+
"""Tool that asks the user for input during agent execution.
|
|
13
|
+
|
|
14
|
+
This enables human-in-the-loop agent workflows where the agent can
|
|
15
|
+
request clarification or additional information from the user.
|
|
16
|
+
|
|
17
|
+
Usage::
|
|
18
|
+
|
|
19
|
+
tool = HumanInputTool()
|
|
20
|
+
# Or with a custom input function:
|
|
21
|
+
tool = HumanInputTool(input_fn=my_custom_input)
|
|
22
|
+
|
|
23
|
+
By default, uses Python's built-in ``input()`` function.
|
|
24
|
+
Pass a custom ``input_fn`` for non-terminal environments (web, API, etc.).
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
name = "human_input"
|
|
28
|
+
description = (
|
|
29
|
+
"Ask the user a question and get their response. "
|
|
30
|
+
"Use this when you need clarification, additional information, "
|
|
31
|
+
"or confirmation from the user before proceeding."
|
|
32
|
+
)
|
|
33
|
+
parameters = {
|
|
34
|
+
"type": "object",
|
|
35
|
+
"properties": {
|
|
36
|
+
"question": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"description": "The question to ask the user",
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
"required": ["question"],
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
def __init__(self, input_fn: Any = None) -> None:
|
|
45
|
+
self._input_fn = input_fn
|
|
46
|
+
|
|
47
|
+
async def run(self, question: str = "", **kwargs: Any) -> ToolResult:
|
|
48
|
+
prompt = question or kwargs.get("input", "")
|
|
49
|
+
if not prompt:
|
|
50
|
+
return ToolResult(output="", error="No question provided.")
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
if self._input_fn is not None:
|
|
54
|
+
result = self._input_fn(prompt)
|
|
55
|
+
if asyncio.iscoroutine(result):
|
|
56
|
+
result = await result
|
|
57
|
+
else:
|
|
58
|
+
loop = asyncio.get_event_loop()
|
|
59
|
+
result = await loop.run_in_executor(None, input, f"\n{prompt}\n> ")
|
|
60
|
+
|
|
61
|
+
return ToolResult(output=str(result))
|
|
62
|
+
except Exception as e:
|
|
63
|
+
return ToolResult(output="", error=f"Failed to get input: {e}")
|
|
@@ -66,7 +66,9 @@ class RegexTool(BaseTool):
|
|
|
66
66
|
|
|
67
67
|
if action == "findall":
|
|
68
68
|
matches = re.findall(pattern, text, re_flags)
|
|
69
|
-
return ToolResult(
|
|
69
|
+
return ToolResult(
|
|
70
|
+
output="\n".join(str(m) for m in matches) if matches else "(no matches)"
|
|
71
|
+
)
|
|
70
72
|
|
|
71
73
|
elif action == "match":
|
|
72
74
|
m = re.match(pattern, text, re_flags)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Wikipedia Tool: search and fetch Wikipedia articles."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
from urllib.parse import quote_plus
|
|
7
|
+
|
|
8
|
+
from ..base import BaseTool, ToolResult
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class WikipediaTool(BaseTool):
|
|
12
|
+
"""Search and fetch Wikipedia article summaries.
|
|
13
|
+
|
|
14
|
+
Uses the Wikipedia REST API — no API key required, no extra dependencies.
|
|
15
|
+
|
|
16
|
+
Usage::
|
|
17
|
+
|
|
18
|
+
tool = WikipediaTool()
|
|
19
|
+
result = await tool.run(query="Python programming language")
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
name = "wikipedia"
|
|
23
|
+
description = (
|
|
24
|
+
"Search Wikipedia and return article summaries. "
|
|
25
|
+
"Input: a search query. "
|
|
26
|
+
"Returns: the title and summary of the most relevant Wikipedia article."
|
|
27
|
+
)
|
|
28
|
+
parameters = {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"properties": {
|
|
31
|
+
"query": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"description": "The search query for Wikipedia",
|
|
34
|
+
},
|
|
35
|
+
"max_results": {
|
|
36
|
+
"type": "integer",
|
|
37
|
+
"description": "Maximum number of articles to return (default: 1)",
|
|
38
|
+
"default": 1,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
"required": ["query"],
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
def __init__(self, max_chars: int = 4000) -> None:
|
|
45
|
+
self._max_chars = max_chars
|
|
46
|
+
|
|
47
|
+
async def run(self, query: str = "", max_results: int = 1, **kwargs: Any) -> ToolResult:
|
|
48
|
+
search_query = query or kwargs.get("input", "")
|
|
49
|
+
if not search_query:
|
|
50
|
+
return ToolResult(output="", error="No search query provided.")
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
import urllib.request
|
|
54
|
+
|
|
55
|
+
# Search for articles
|
|
56
|
+
search_url = (
|
|
57
|
+
f"https://en.wikipedia.org/w/api.php?action=opensearch"
|
|
58
|
+
f"&search={quote_plus(search_query)}&limit={max_results}&format=json"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
import asyncio
|
|
62
|
+
import json
|
|
63
|
+
|
|
64
|
+
loop = asyncio.get_event_loop()
|
|
65
|
+
|
|
66
|
+
def _fetch_search():
|
|
67
|
+
req = urllib.request.Request(search_url, headers={"User-Agent": "SynapseKit/1.0"})
|
|
68
|
+
with urllib.request.urlopen(req, timeout=10) as resp:
|
|
69
|
+
return json.loads(resp.read().decode())
|
|
70
|
+
|
|
71
|
+
search_data = await loop.run_in_executor(None, _fetch_search)
|
|
72
|
+
|
|
73
|
+
if len(search_data) < 2 or not search_data[1]:
|
|
74
|
+
return ToolResult(output="No Wikipedia articles found.")
|
|
75
|
+
|
|
76
|
+
results = []
|
|
77
|
+
titles = search_data[1][:max_results]
|
|
78
|
+
|
|
79
|
+
for title in titles:
|
|
80
|
+
# Fetch article summary
|
|
81
|
+
summary_url = (
|
|
82
|
+
f"https://en.wikipedia.org/api/rest_v1/page/summary/{quote_plus(title)}"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def _fetch_summary(url=summary_url):
|
|
86
|
+
req = urllib.request.Request(url, headers={"User-Agent": "SynapseKit/1.0"})
|
|
87
|
+
with urllib.request.urlopen(req, timeout=10) as resp:
|
|
88
|
+
return json.loads(resp.read().decode())
|
|
89
|
+
|
|
90
|
+
summary_data = await loop.run_in_executor(None, _fetch_summary)
|
|
91
|
+
|
|
92
|
+
article_title = summary_data.get("title", title)
|
|
93
|
+
extract = summary_data.get("extract", "No summary available.")
|
|
94
|
+
url = summary_data.get("content_urls", {}).get("desktop", {}).get("page", "")
|
|
95
|
+
|
|
96
|
+
if len(extract) > self._max_chars:
|
|
97
|
+
extract = extract[: self._max_chars] + "..."
|
|
98
|
+
|
|
99
|
+
results.append(f"**{article_title}**\n{url}\n\n{extract}")
|
|
100
|
+
|
|
101
|
+
return ToolResult(output="\n\n---\n\n".join(results))
|
|
102
|
+
except Exception as e:
|
|
103
|
+
return ToolResult(output="", error=f"Wikipedia search failed: {e}")
|
|
@@ -3,8 +3,10 @@ from .compiled import CompiledGraph
|
|
|
3
3
|
from .edge import ConditionalEdge, ConditionFn, Edge
|
|
4
4
|
from .errors import GraphConfigError, GraphRuntimeError
|
|
5
5
|
from .graph import StateGraph
|
|
6
|
-
from .
|
|
6
|
+
from .interrupt import GraphInterrupt, InterruptState
|
|
7
|
+
from .node import Node, NodeFn, agent_node, llm_node, rag_node
|
|
7
8
|
from .state import END, GraphState
|
|
9
|
+
from .subgraph import subgraph_node
|
|
8
10
|
|
|
9
11
|
__all__ = [
|
|
10
12
|
"END",
|
|
@@ -14,13 +16,17 @@ __all__ = [
|
|
|
14
16
|
"ConditionalEdge",
|
|
15
17
|
"Edge",
|
|
16
18
|
"GraphConfigError",
|
|
19
|
+
"GraphInterrupt",
|
|
17
20
|
"GraphRuntimeError",
|
|
18
21
|
"GraphState",
|
|
19
22
|
"InMemoryCheckpointer",
|
|
23
|
+
"InterruptState",
|
|
20
24
|
"Node",
|
|
21
25
|
"NodeFn",
|
|
22
26
|
"SQLiteCheckpointer",
|
|
23
27
|
"StateGraph",
|
|
24
28
|
"agent_node",
|
|
29
|
+
"llm_node",
|
|
25
30
|
"rag_node",
|
|
31
|
+
"subgraph_node",
|
|
26
32
|
]
|
|
@@ -7,6 +7,7 @@ from typing import TYPE_CHECKING, Any
|
|
|
7
7
|
|
|
8
8
|
from .edge import ConditionalEdge, Edge
|
|
9
9
|
from .errors import GraphRuntimeError
|
|
10
|
+
from .interrupt import GraphInterrupt
|
|
10
11
|
from .mermaid import get_mermaid
|
|
11
12
|
from .state import END
|
|
12
13
|
|
|
@@ -71,12 +72,22 @@ class CompiledGraph:
|
|
|
71
72
|
self,
|
|
72
73
|
graph_id: str,
|
|
73
74
|
checkpointer: BaseCheckpointer,
|
|
75
|
+
updates: dict[str, Any] | None = None,
|
|
74
76
|
) -> dict[str, Any]:
|
|
75
|
-
"""Resume execution from a checkpointed state.
|
|
77
|
+
"""Resume execution from a checkpointed state.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
graph_id: The graph execution ID to resume.
|
|
81
|
+
checkpointer: The checkpointer that holds the saved state.
|
|
82
|
+
updates: Optional state updates to apply before resuming
|
|
83
|
+
(e.g. human-provided edits after a ``GraphInterrupt``).
|
|
84
|
+
"""
|
|
76
85
|
saved = checkpointer.load(graph_id)
|
|
77
86
|
if saved is None:
|
|
78
87
|
raise GraphRuntimeError(f"No checkpoint found for graph_id={graph_id!r}.")
|
|
79
88
|
_step, state = saved
|
|
89
|
+
if updates:
|
|
90
|
+
state.update(updates)
|
|
80
91
|
return await self.run(state, checkpointer=checkpointer, graph_id=graph_id)
|
|
81
92
|
|
|
82
93
|
def run_sync(
|
|
@@ -90,6 +101,62 @@ class CompiledGraph:
|
|
|
90
101
|
|
|
91
102
|
return run_sync(self.run(state, checkpointer=checkpointer, graph_id=graph_id))
|
|
92
103
|
|
|
104
|
+
async def stream_tokens(
|
|
105
|
+
self,
|
|
106
|
+
state: dict[str, Any],
|
|
107
|
+
) -> AsyncGenerator[dict[str, Any]]:
|
|
108
|
+
"""Yield token-level events from LLM nodes.
|
|
109
|
+
|
|
110
|
+
Yields dicts with either:
|
|
111
|
+
- ``{"type": "token", "node": name, "token": str}`` for streaming tokens
|
|
112
|
+
- ``{"type": "node_complete", "node": name, "state": dict}`` for non-streaming nodes
|
|
113
|
+
|
|
114
|
+
LLM nodes are detected by checking if the node function's return dict
|
|
115
|
+
contains a ``"__stream__"`` key with an async generator.
|
|
116
|
+
"""
|
|
117
|
+
state = dict(state)
|
|
118
|
+
graph = self._graph
|
|
119
|
+
current_wave: list[str] = [graph._entry_point] # type: ignore[list-item]
|
|
120
|
+
steps = 0
|
|
121
|
+
|
|
122
|
+
while current_wave:
|
|
123
|
+
if steps >= self._max_steps:
|
|
124
|
+
raise GraphRuntimeError(
|
|
125
|
+
f"Graph exceeded _MAX_STEPS={self._max_steps}. "
|
|
126
|
+
"Check for infinite loops in conditional edges."
|
|
127
|
+
)
|
|
128
|
+
steps += 1
|
|
129
|
+
|
|
130
|
+
for name in current_wave:
|
|
131
|
+
node = graph._nodes.get(name)
|
|
132
|
+
if node is None:
|
|
133
|
+
raise GraphRuntimeError(f"Node {name!r} not found in graph.")
|
|
134
|
+
|
|
135
|
+
result = node.fn(state)
|
|
136
|
+
if inspect.isawaitable(result):
|
|
137
|
+
result = await result
|
|
138
|
+
|
|
139
|
+
if not isinstance(result, dict):
|
|
140
|
+
raise GraphRuntimeError(
|
|
141
|
+
f"Node {name!r} must return a dict, got {type(result).__name__!r}."
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Check for streaming token generator
|
|
145
|
+
stream_gen = result.pop("__stream__", None)
|
|
146
|
+
if stream_gen is not None:
|
|
147
|
+
collected: list[str] = []
|
|
148
|
+
async for token in stream_gen:
|
|
149
|
+
collected.append(token)
|
|
150
|
+
yield {"type": "token", "node": name, "token": token}
|
|
151
|
+
# Store the full text in the result
|
|
152
|
+
if "__stream_key__" in result:
|
|
153
|
+
result[result.pop("__stream_key__")] = "".join(collected)
|
|
154
|
+
|
|
155
|
+
state.update(result)
|
|
156
|
+
yield {"type": "node_complete", "node": name, "state": dict(state)}
|
|
157
|
+
|
|
158
|
+
current_wave = await self._next_wave(current_wave, state)
|
|
159
|
+
|
|
93
160
|
def get_mermaid(self) -> str:
|
|
94
161
|
return get_mermaid(self._graph)
|
|
95
162
|
|
|
@@ -116,7 +183,15 @@ class CompiledGraph:
|
|
|
116
183
|
steps += 1
|
|
117
184
|
|
|
118
185
|
# Run all nodes in this wave concurrently
|
|
119
|
-
|
|
186
|
+
try:
|
|
187
|
+
results = await asyncio.gather(
|
|
188
|
+
*[self._call_node(name, state) for name in current_wave]
|
|
189
|
+
)
|
|
190
|
+
except GraphInterrupt as exc:
|
|
191
|
+
# Save state and raise InterruptState for the caller
|
|
192
|
+
if checkpointer is not None and graph_id is not None:
|
|
193
|
+
checkpointer.save(graph_id, steps, dict(state))
|
|
194
|
+
raise GraphInterrupt(exc.message, exc.data) from None
|
|
120
195
|
|
|
121
196
|
# Merge partial results into state and yield events
|
|
122
197
|
for name, partial in zip(current_wave, results, strict=False):
|