spaik-sdk 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.
- spaik_sdk-0.6.2/.cursor/rules/global.mdc +32 -0
- spaik_sdk-0.6.2/.cursor/rules/post_run.mdc +14 -0
- spaik_sdk-0.6.2/.cursor/rules/repo_overview.mdc +84 -0
- spaik_sdk-0.6.2/.cursor/rules/testing-structure.mdc +54 -0
- spaik_sdk-0.6.2/.gitignore +41 -0
- spaik_sdk-0.6.2/Makefile +92 -0
- spaik_sdk-0.6.2/PKG-INFO +379 -0
- spaik_sdk-0.6.2/README.md +327 -0
- spaik_sdk-0.6.2/__init__.py.bak +20 -0
- spaik_sdk-0.6.2/env.example +28 -0
- spaik_sdk-0.6.2/kill.sh +39 -0
- spaik_sdk-0.6.2/py.typed +1 -0
- spaik_sdk-0.6.2/pyproject.toml +146 -0
- spaik_sdk-0.6.2/setup.sh +11 -0
- spaik_sdk-0.6.2/spaik_sdk/__init__.py +21 -0
- spaik_sdk-0.6.2/spaik_sdk/agent/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/agent/base_agent.py +249 -0
- spaik_sdk-0.6.2/spaik_sdk/attachments/__init__.py +22 -0
- spaik_sdk-0.6.2/spaik_sdk/attachments/builder.py +61 -0
- spaik_sdk-0.6.2/spaik_sdk/attachments/file_storage_provider.py +27 -0
- spaik_sdk-0.6.2/spaik_sdk/attachments/mime_types.py +118 -0
- spaik_sdk-0.6.2/spaik_sdk/attachments/models.py +63 -0
- spaik_sdk-0.6.2/spaik_sdk/attachments/provider_support.py +53 -0
- spaik_sdk-0.6.2/spaik_sdk/attachments/storage/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/attachments/storage/base_file_storage.py +32 -0
- spaik_sdk-0.6.2/spaik_sdk/attachments/storage/impl/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/attachments/storage/impl/local_file_storage.py +101 -0
- spaik_sdk-0.6.2/spaik_sdk/audio/__init__.py +12 -0
- spaik_sdk-0.6.2/spaik_sdk/audio/options.py +53 -0
- spaik_sdk-0.6.2/spaik_sdk/audio/providers/__init__.py +1 -0
- spaik_sdk-0.6.2/spaik_sdk/audio/providers/google_tts.py +77 -0
- spaik_sdk-0.6.2/spaik_sdk/audio/providers/openai_stt.py +71 -0
- spaik_sdk-0.6.2/spaik_sdk/audio/providers/openai_tts.py +111 -0
- spaik_sdk-0.6.2/spaik_sdk/audio/stt.py +61 -0
- spaik_sdk-0.6.2/spaik_sdk/audio/tts.py +124 -0
- spaik_sdk-0.6.2/spaik_sdk/config/credentials_provider.py +10 -0
- spaik_sdk-0.6.2/spaik_sdk/config/env.py +59 -0
- spaik_sdk-0.6.2/spaik_sdk/config/env_credentials_provider.py +7 -0
- spaik_sdk-0.6.2/spaik_sdk/config/get_credentials_provider.py +14 -0
- spaik_sdk-0.6.2/spaik_sdk/image_gen/__init__.py +9 -0
- spaik_sdk-0.6.2/spaik_sdk/image_gen/image_generator.py +83 -0
- spaik_sdk-0.6.2/spaik_sdk/image_gen/options.py +24 -0
- spaik_sdk-0.6.2/spaik_sdk/image_gen/providers/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/image_gen/providers/google.py +75 -0
- spaik_sdk-0.6.2/spaik_sdk/image_gen/providers/openai.py +60 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/cancellation_handle.py +10 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/consumption/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/consumption/consumption_estimate.py +26 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/consumption/consumption_estimate_builder.py +113 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/consumption/consumption_extractor.py +59 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/consumption/token_usage.py +31 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/converters.py +146 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/cost/__init__.py +1 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/cost/builtin_cost_provider.py +83 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/cost/cost_estimate.py +8 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/cost/cost_provider.py +28 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/extract_error_message.py +37 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/langchain_loop_manager.py +270 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/langchain_service.py +196 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/message_handler.py +188 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/streaming/__init__.py +1 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/streaming/block_manager.py +152 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/streaming/models.py +42 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/streaming/streaming_content_handler.py +157 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/streaming/streaming_event_handler.py +215 -0
- spaik_sdk-0.6.2/spaik_sdk/llm/streaming/streaming_state_manager.py +58 -0
- spaik_sdk-0.6.2/spaik_sdk/models/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/models/factories/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/models/factories/anthropic_factory.py +33 -0
- spaik_sdk-0.6.2/spaik_sdk/models/factories/base_model_factory.py +71 -0
- spaik_sdk-0.6.2/spaik_sdk/models/factories/google_factory.py +30 -0
- spaik_sdk-0.6.2/spaik_sdk/models/factories/ollama_factory.py +41 -0
- spaik_sdk-0.6.2/spaik_sdk/models/factories/openai_factory.py +50 -0
- spaik_sdk-0.6.2/spaik_sdk/models/llm_config.py +46 -0
- spaik_sdk-0.6.2/spaik_sdk/models/llm_families.py +7 -0
- spaik_sdk-0.6.2/spaik_sdk/models/llm_model.py +17 -0
- spaik_sdk-0.6.2/spaik_sdk/models/llm_wrapper.py +25 -0
- spaik_sdk-0.6.2/spaik_sdk/models/model_registry.py +156 -0
- spaik_sdk-0.6.2/spaik_sdk/models/providers/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/models/providers/anthropic_provider.py +29 -0
- spaik_sdk-0.6.2/spaik_sdk/models/providers/azure_provider.py +31 -0
- spaik_sdk-0.6.2/spaik_sdk/models/providers/base_provider.py +62 -0
- spaik_sdk-0.6.2/spaik_sdk/models/providers/google_provider.py +26 -0
- spaik_sdk-0.6.2/spaik_sdk/models/providers/ollama_provider.py +26 -0
- spaik_sdk-0.6.2/spaik_sdk/models/providers/openai_provider.py +26 -0
- spaik_sdk-0.6.2/spaik_sdk/models/providers/provider_type.py +90 -0
- spaik_sdk-0.6.2/spaik_sdk/orchestration/__init__.py +24 -0
- spaik_sdk-0.6.2/spaik_sdk/orchestration/base_orchestrator.py +238 -0
- spaik_sdk-0.6.2/spaik_sdk/orchestration/checkpoint.py +80 -0
- spaik_sdk-0.6.2/spaik_sdk/orchestration/models.py +103 -0
- spaik_sdk-0.6.2/spaik_sdk/prompt/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/prompt/get_prompt_loader.py +13 -0
- spaik_sdk-0.6.2/spaik_sdk/prompt/local_prompt_loader.py +21 -0
- spaik_sdk-0.6.2/spaik_sdk/prompt/prompt_loader.py +48 -0
- spaik_sdk-0.6.2/spaik_sdk/prompt/prompt_loader_mode.py +14 -0
- spaik_sdk-0.6.2/spaik_sdk/py.typed +1 -0
- spaik_sdk-0.6.2/spaik_sdk/recording/__init__.py +1 -0
- spaik_sdk-0.6.2/spaik_sdk/recording/base_playback.py +90 -0
- spaik_sdk-0.6.2/spaik_sdk/recording/base_recorder.py +50 -0
- spaik_sdk-0.6.2/spaik_sdk/recording/conditional_recorder.py +38 -0
- spaik_sdk-0.6.2/spaik_sdk/recording/impl/__init__.py +1 -0
- spaik_sdk-0.6.2/spaik_sdk/recording/impl/local_playback.py +76 -0
- spaik_sdk-0.6.2/spaik_sdk/recording/impl/local_recorder.py +85 -0
- spaik_sdk-0.6.2/spaik_sdk/recording/langchain_serializer.py +88 -0
- spaik_sdk-0.6.2/spaik_sdk/server/__init__.py +1 -0
- spaik_sdk-0.6.2/spaik_sdk/server/api/routers/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/server/api/routers/api_builder.py +149 -0
- spaik_sdk-0.6.2/spaik_sdk/server/api/routers/audio_router_factory.py +201 -0
- spaik_sdk-0.6.2/spaik_sdk/server/api/routers/file_router_factory.py +111 -0
- spaik_sdk-0.6.2/spaik_sdk/server/api/routers/thread_router_factory.py +284 -0
- spaik_sdk-0.6.2/spaik_sdk/server/api/streaming/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/server/api/streaming/format_sse_event.py +41 -0
- spaik_sdk-0.6.2/spaik_sdk/server/api/streaming/negotiate_streaming_response.py +8 -0
- spaik_sdk-0.6.2/spaik_sdk/server/api/streaming/streaming_negotiator.py +10 -0
- spaik_sdk-0.6.2/spaik_sdk/server/authorization/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/server/authorization/base_authorizer.py +64 -0
- spaik_sdk-0.6.2/spaik_sdk/server/authorization/base_user.py +13 -0
- spaik_sdk-0.6.2/spaik_sdk/server/authorization/dummy_authorizer.py +17 -0
- spaik_sdk-0.6.2/spaik_sdk/server/job_processor/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/server/job_processor/base_job_processor.py +8 -0
- spaik_sdk-0.6.2/spaik_sdk/server/job_processor/thread_job_processor.py +32 -0
- spaik_sdk-0.6.2/spaik_sdk/server/pubsub/__init__.py +1 -0
- spaik_sdk-0.6.2/spaik_sdk/server/pubsub/cancellation_publisher.py +7 -0
- spaik_sdk-0.6.2/spaik_sdk/server/pubsub/cancellation_subscriber.py +38 -0
- spaik_sdk-0.6.2/spaik_sdk/server/pubsub/event_publisher.py +13 -0
- spaik_sdk-0.6.2/spaik_sdk/server/pubsub/impl/__init__.py +1 -0
- spaik_sdk-0.6.2/spaik_sdk/server/pubsub/impl/local_cancellation_pubsub.py +48 -0
- spaik_sdk-0.6.2/spaik_sdk/server/pubsub/impl/signalr_publisher.py +36 -0
- spaik_sdk-0.6.2/spaik_sdk/server/queue/__init__.py +1 -0
- spaik_sdk-0.6.2/spaik_sdk/server/queue/agent_job_queue.py +27 -0
- spaik_sdk-0.6.2/spaik_sdk/server/queue/impl/__init__.py +1 -0
- spaik_sdk-0.6.2/spaik_sdk/server/queue/impl/azure_queue.py +24 -0
- spaik_sdk-0.6.2/spaik_sdk/server/response/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/server/response/agent_response_generator.py +39 -0
- spaik_sdk-0.6.2/spaik_sdk/server/response/response_generator.py +13 -0
- spaik_sdk-0.6.2/spaik_sdk/server/response/simple_agent_response_generator.py +14 -0
- spaik_sdk-0.6.2/spaik_sdk/server/services/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/server/services/thread_converters.py +113 -0
- spaik_sdk-0.6.2/spaik_sdk/server/services/thread_models.py +90 -0
- spaik_sdk-0.6.2/spaik_sdk/server/services/thread_service.py +91 -0
- spaik_sdk-0.6.2/spaik_sdk/server/storage/__init__.py +1 -0
- spaik_sdk-0.6.2/spaik_sdk/server/storage/base_thread_repository.py +51 -0
- spaik_sdk-0.6.2/spaik_sdk/server/storage/impl/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/server/storage/impl/in_memory_thread_repository.py +100 -0
- spaik_sdk-0.6.2/spaik_sdk/server/storage/impl/local_file_thread_repository.py +217 -0
- spaik_sdk-0.6.2/spaik_sdk/server/storage/thread_filter.py +166 -0
- spaik_sdk-0.6.2/spaik_sdk/server/storage/thread_metadata.py +53 -0
- spaik_sdk-0.6.2/spaik_sdk/thread/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/thread/adapters/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/thread/adapters/cli/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/thread/adapters/cli/block_display.py +92 -0
- spaik_sdk-0.6.2/spaik_sdk/thread/adapters/cli/display_manager.py +84 -0
- spaik_sdk-0.6.2/spaik_sdk/thread/adapters/cli/live_cli.py +235 -0
- spaik_sdk-0.6.2/spaik_sdk/thread/adapters/event_adapter.py +28 -0
- spaik_sdk-0.6.2/spaik_sdk/thread/adapters/streaming_block_adapter.py +57 -0
- spaik_sdk-0.6.2/spaik_sdk/thread/adapters/sync_adapter.py +76 -0
- spaik_sdk-0.6.2/spaik_sdk/thread/models.py +224 -0
- spaik_sdk-0.6.2/spaik_sdk/thread/thread_container.py +468 -0
- spaik_sdk-0.6.2/spaik_sdk/tools/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/tools/impl/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/tools/impl/mcp_tool_provider.py +93 -0
- spaik_sdk-0.6.2/spaik_sdk/tools/impl/search_tool_provider.py +18 -0
- spaik_sdk-0.6.2/spaik_sdk/tools/tool_provider.py +131 -0
- spaik_sdk-0.6.2/spaik_sdk/tracing/__init__.py +13 -0
- spaik_sdk-0.6.2/spaik_sdk/tracing/agent_trace.py +72 -0
- spaik_sdk-0.6.2/spaik_sdk/tracing/get_trace_sink.py +15 -0
- spaik_sdk-0.6.2/spaik_sdk/tracing/local_trace_sink.py +23 -0
- spaik_sdk-0.6.2/spaik_sdk/tracing/trace_sink.py +19 -0
- spaik_sdk-0.6.2/spaik_sdk/tracing/trace_sink_mode.py +14 -0
- spaik_sdk-0.6.2/spaik_sdk/utils/__init__.py +0 -0
- spaik_sdk-0.6.2/spaik_sdk/utils/init_logger.py +24 -0
- spaik_sdk-0.6.2/tests/__init__.py +0 -0
- spaik_sdk-0.6.2/tests/conftest.py +0 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_consumption_tracking/1.jsonl +6 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_event_stream_basic/1.jsonl +6 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_text/1.jsonl +6 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_text_claude-3-7-sonnet-latest/1.jsonl +6 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_text_claude-haiku-4-5-20251001/1.jsonl +6 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_text_claude-opus-4-5-20251101/1.jsonl +32 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_text_claude-sonnet-4-20250514/1.jsonl +6 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_text_claude-sonnet-4-5-20250929/1.jsonl +6 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_text_gemini-2.5-flash/1.jsonl +6 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_text_gemini-3-flash-preview/1.jsonl +19 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_text_gemini-3-pro-preview/1.jsonl +20 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_text_gpt-4.1/1.jsonl +6 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_text_gpt-5.1/1.jsonl +31 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_text_gpt-5.2/1.jsonl +30 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_text_o4-mini/1.jsonl +6 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_text_with_cancellation/1.jsonl +6 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_with_tool_call_claude-3-7-sonnet-latest/1.jsonl +16 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_with_tool_call_claude-haiku-4-5-20251001/1.jsonl +16 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_with_tool_call_claude-opus-4-1-20250805/1.jsonl +16 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_with_tool_call_claude-opus-4-5-20251101/1.jsonl +53 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_with_tool_call_claude-sonnet-4-20250514/1.jsonl +16 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_with_tool_call_claude-sonnet-4-5-20250929/1.jsonl +16 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_with_tool_call_gemini-2.5-flash/1.jsonl +16 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_with_tool_call_gemini-3-flash-preview/1.jsonl +44 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_with_tool_call_gpt-4.1/1.jsonl +16 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_with_tool_call_gpt-5/1.jsonl +16 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_with_tool_call_gpt-5.1/1.jsonl +142 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_with_tool_call_gpt-5.1-codex/1.jsonl +64 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_with_tool_call_gpt-5.2/1.jsonl +58 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_with_tool_call_gpt-5.2-pro/1.jsonl +46 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_response_with_tool_call_o4-mini/1.jsonl +16 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_get_structured_response/1.json +4 -0
- spaik_sdk-0.6.2/tests/data/recordings/test_mystery_streaming_issue/1.jsonl +6 -0
- spaik_sdk-0.6.2/tests/integration/__init__.py +0 -0
- spaik_sdk-0.6.2/tests/integration/test_cost_tracking_integration.py +79 -0
- spaik_sdk-0.6.2/tests/integration/test_mcp_tool_provider.py +68 -0
- spaik_sdk-0.6.2/tests/manual/__init__.py +0 -0
- spaik_sdk-0.6.2/tests/manual/test_search.py +22 -0
- spaik_sdk-0.6.2/tests/unit/__init__.py +0 -0
- spaik_sdk-0.6.2/tests/unit/spaik_sdk/agent/test_base_agent.py +277 -0
- spaik_sdk-0.6.2/tests/unit/spaik_sdk/llm/streaming/test_streaming_event_handler.py +221 -0
- spaik_sdk-0.6.2/tests/unit/spaik_sdk/models/__init__.py +0 -0
- spaik_sdk-0.6.2/tests/unit/spaik_sdk/models/test_model_registry.py +76 -0
- spaik_sdk-0.6.2/tests/unit/spaik_sdk/orchestration/__init__.py +0 -0
- spaik_sdk-0.6.2/tests/unit/spaik_sdk/orchestration/test_base_orchestrator.py +289 -0
- spaik_sdk-0.6.2/tests/unit/spaik_sdk/tools/impl/__init__.py +0 -0
- spaik_sdk-0.6.2/uv.lock +3254 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
description:
|
|
3
|
+
globs:
|
|
4
|
+
alwaysApply: true
|
|
5
|
+
---
|
|
6
|
+
# General
|
|
7
|
+
|
|
8
|
+
this project is a helper SDK for building various kinds of agentic + ai solutions
|
|
9
|
+
|
|
10
|
+
main goal is to give good DX and make it fast and easy to build POCs that also are of good quality
|
|
11
|
+
and something you can easily get to production as well
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# style stuff:
|
|
15
|
+
|
|
16
|
+
- use absolute imports
|
|
17
|
+
- comments are a code smell, avoid docstrings too if not adding anything
|
|
18
|
+
- empty __init__.py unless otherwise specified
|
|
19
|
+
- no star imports
|
|
20
|
+
- always use types
|
|
21
|
+
- use uv and ruff
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# logging
|
|
25
|
+
|
|
26
|
+
this is the way to obtain the logger:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
from spaik_sdk.utils.init_logger import init_logger
|
|
30
|
+
|
|
31
|
+
logger = init_logger(__name__)
|
|
32
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
description:
|
|
3
|
+
globs:
|
|
4
|
+
alwaysApply: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
After you have done something substantial (eg. touching multiple files):
|
|
8
|
+
|
|
9
|
+
First, take a look at the code you wrote. Does it look clean an elegant with good separation of concerns and small files and small methods. If not, go clean it up. Also make sure you don't have any inline imports etc smelly stuff.
|
|
10
|
+
|
|
11
|
+
Then run this command to make sure everything is ok:
|
|
12
|
+
|
|
13
|
+
`make lint-fix && make typecheck && make test-unit`
|
|
14
|
+
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
description:
|
|
3
|
+
globs:
|
|
4
|
+
alwaysApply: true
|
|
5
|
+
---
|
|
6
|
+
# Agent SDK - Repository Overview
|
|
7
|
+
|
|
8
|
+
This is a Python-based Agent SDK for building various kinds of agentic + AI solutions. The main goal is to provide good DX (Developer Experience) and make it fast and easy to build POCs that are also of good quality and can easily transition to production.
|
|
9
|
+
|
|
10
|
+
## Core Purpose & Philosophy
|
|
11
|
+
|
|
12
|
+
- **Helper SDK** for building agentic + AI solutions
|
|
13
|
+
- **Fast POC Development** that scales to production quality
|
|
14
|
+
- **Good DX** with clean abstractions and patterns
|
|
15
|
+
- **Multi-LLM Support** with unified interface
|
|
16
|
+
- **Streaming & Interactive** capabilities built-in
|
|
17
|
+
|
|
18
|
+
## Project Structure & Key Components
|
|
19
|
+
|
|
20
|
+
### Entry Points
|
|
21
|
+
- **[main.py](mdc:main.py)** - Main application entry point, demonstrates DemoAgent and MinimalAgent usage
|
|
22
|
+
- **[setup.sh](mdc:setup.sh)** - Development environment setup script
|
|
23
|
+
|
|
24
|
+
### Core Architecture Modules
|
|
25
|
+
|
|
26
|
+
#### **Agent System** (`app/agent/`)
|
|
27
|
+
- **[app/agent/base_agent.py](mdc:app/agent/base_agent.py)** - BaseAgent class providing foundation for all agents
|
|
28
|
+
- **[app/demo_agents/](mdc:app/demo_agents)** - Example agent implementations (DemoAgent, MinimalAgent)
|
|
29
|
+
|
|
30
|
+
#### **LLM Integration** (`app/models/`)
|
|
31
|
+
Multi-provider LLM support with factory pattern:
|
|
32
|
+
- **Supported Providers**: Anthropic (Claude), OpenAI (GPT), Google (Gemini), Azure AI Foundry
|
|
33
|
+
- **[app/models/llm_types.py](mdc:app/models/llm_types.py)** - Model enums and provider types
|
|
34
|
+
- **[app/models/llm_config.py](mdc:app/models/llm_config.py)** - LLM configuration dataclass
|
|
35
|
+
- **[app/models/factories/](mdc:app/models/factories)** - Model factory implementations per provider
|
|
36
|
+
- **[app/models/providers/](mdc:app/models/providers)** - Provider-specific API integrations
|
|
37
|
+
|
|
38
|
+
#### **LangChain Service Layer** (`app/llm/`)
|
|
39
|
+
- **[app/llm/langchain_service.py](mdc:app/llm/langchain_service.py)** - Core service wrapping LangChain functionality
|
|
40
|
+
- **[app/llm/message_handler.py](mdc:app/llm/message_handler.py)** - Conversation history and message processing
|
|
41
|
+
- **[app/llm/streaming/](mdc:app/llm/streaming)** - Streaming response handling components
|
|
42
|
+
|
|
43
|
+
#### **Thread Management** (`app/thread/`)
|
|
44
|
+
- **[app/thread/thread_container.py](mdc:app/thread/thread_container.py)** - Core conversation state management
|
|
45
|
+
- **[app/thread/models.py](mdc:app/thread/models.py)** - Message, block, and event data models
|
|
46
|
+
- **[app/thread/adapters/](mdc:app/thread/adapters)** - Different interaction interfaces (sync, CLI)
|
|
47
|
+
|
|
48
|
+
#### **Tool System** (`app/tools/`)
|
|
49
|
+
- **[app/tools/tool_provider.py](mdc:app/tools/tool_provider.py)** - Abstract base for creating agent tools
|
|
50
|
+
- Supports LangChain BaseTool integration with Pydantic schemas
|
|
51
|
+
|
|
52
|
+
#### **Configuration & Infrastructure**
|
|
53
|
+
- **[config/env.py](mdc:config/env.py)** - Environment variable management
|
|
54
|
+
- **[app/prompt/prompt_loader.py](mdc:app/prompt/prompt_loader.py)** - Static prompt loading from markdown
|
|
55
|
+
- **[app/tracing/agent_trace.py](mdc:app/tracing/agent_trace.py)** - Execution tracing for debugging
|
|
56
|
+
- **[app/utils/init_logger.py](mdc:app/utils/init_logger.py)** - Logging utilities
|
|
57
|
+
|
|
58
|
+
## Key Technologies & Dependencies
|
|
59
|
+
|
|
60
|
+
- **Python 3.10+** with modern async/await patterns
|
|
61
|
+
- **LangChain** - Primary framework for LLM orchestration
|
|
62
|
+
- **LangGraph** - For stateful, multi-actor applications
|
|
63
|
+
- **FastAPI** - Web framework for API endpoints
|
|
64
|
+
- **Pydantic** - Data validation and settings management
|
|
65
|
+
- **uv** - Fast Python package manager and runner
|
|
66
|
+
- **ruff** - Fast Python linter and formatter
|
|
67
|
+
|
|
68
|
+
## Architectural Patterns
|
|
69
|
+
|
|
70
|
+
1. **Agent-Based Architecture** - BaseAgent foundation with concrete implementations
|
|
71
|
+
2. **Factory Pattern** - For LLM provider instantiation
|
|
72
|
+
3. **Provider Pattern** - Unified interface across different LLM APIs
|
|
73
|
+
4. **Event-Driven** - ThreadContainer publishes events for real-time updates
|
|
74
|
+
5. **Streaming-First** - Built-in support for streaming responses
|
|
75
|
+
6. **Tool Integration** - LangChain tool ecosystem integration
|
|
76
|
+
7. **Modular Design** - Clear separation of concerns across modules
|
|
77
|
+
|
|
78
|
+
## Development Standards
|
|
79
|
+
|
|
80
|
+
- **Absolute imports** - No relative imports
|
|
81
|
+
- **Type annotations** - Always use types
|
|
82
|
+
- **Minimal comments** - Code should be self-documenting
|
|
83
|
+
- **Empty `__init__.py`** - Unless otherwise specified
|
|
84
|
+
- **uv + ruff** - Package management and linting
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
description:
|
|
3
|
+
globs:
|
|
4
|
+
alwaysApply: true
|
|
5
|
+
---
|
|
6
|
+
# Testing Structure
|
|
7
|
+
|
|
8
|
+
## Unit Tests
|
|
9
|
+
- Location: `tests/unit/{folder path to file being tested}/test_{filename}.py`
|
|
10
|
+
- Example: For `spaik_sdk/agent/base_agent.py` → `tests/unit/spaik_sdk/agent/test_base_agent.py`
|
|
11
|
+
- Use `@pytest.mark.unit` decorator
|
|
12
|
+
- Mock external dependencies
|
|
13
|
+
|
|
14
|
+
## Integration Tests
|
|
15
|
+
- Location: `tests/integration/` (freeform structure)
|
|
16
|
+
- Use `@pytest.mark.integration` decorator
|
|
17
|
+
- Test complete workflows and component interactions
|
|
18
|
+
|
|
19
|
+
## Test Configuration
|
|
20
|
+
- Main config in [pyproject.toml](mdc:pyproject.toml) under `[tool.pytest.ini_options]`
|
|
21
|
+
- Shared fixtures in [tests/conftest.py](mdc:tests/conftest.py)
|
|
22
|
+
- Test utilities in [tests/utils.py](mdc:tests/utils.py)
|
|
23
|
+
|
|
24
|
+
## Running Tests
|
|
25
|
+
- Use [Makefile](mdc:Makefile) commands: `make test`, `make test-unit`, `make test-integration`
|
|
26
|
+
- Or directly: `uv run pytest`
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
## Philosophy
|
|
30
|
+
- test the public interface, not the implementation and how its coded
|
|
31
|
+
- don't add stupid do-nothing tests that just check completely trivial aspects
|
|
32
|
+
|
|
33
|
+
Example offender:
|
|
34
|
+
```
|
|
35
|
+
def test_valid_config_creation(self):
|
|
36
|
+
"""Test creating a valid LLMConfig."""
|
|
37
|
+
config = LLMConfig(
|
|
38
|
+
model=LLMModel.CLAUDE_3_HAIKU,
|
|
39
|
+
provider_type=ProviderType.ANTHROPIC,
|
|
40
|
+
reasoning=True,
|
|
41
|
+
tool_usage=False
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
assert config.model == LLMModel.CLAUDE_3_HAIKU
|
|
45
|
+
assert config.provider_type == ProviderType.ANTHROPIC
|
|
46
|
+
assert config.reasoning is True
|
|
47
|
+
assert config.tool_usage is False
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The above is 100% pollution in the codebase and gives us absolutely no guarantees of things working.
|
|
51
|
+
|
|
52
|
+
Focus on corner cases and things that might actually break. Do NOT aim for 100% coverage or any such nonsense.
|
|
53
|
+
|
|
54
|
+
We want to test things that might actually break and test them well (not just the happy paths)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules
|
|
3
|
+
.pnp
|
|
4
|
+
.pnp.js
|
|
5
|
+
|
|
6
|
+
# Production
|
|
7
|
+
# dist
|
|
8
|
+
build
|
|
9
|
+
|
|
10
|
+
# Testing
|
|
11
|
+
coverage
|
|
12
|
+
|
|
13
|
+
# Misc
|
|
14
|
+
.DS_Store
|
|
15
|
+
.env
|
|
16
|
+
.env.local
|
|
17
|
+
.env.development.local
|
|
18
|
+
.env.test.local
|
|
19
|
+
.env.production.local
|
|
20
|
+
|
|
21
|
+
# Logs
|
|
22
|
+
npm-debug.log*
|
|
23
|
+
yarn-debug.log*
|
|
24
|
+
yarn-error.log*
|
|
25
|
+
|
|
26
|
+
# Editor
|
|
27
|
+
.vscode/*
|
|
28
|
+
!.vscode/extensions.json
|
|
29
|
+
.idea
|
|
30
|
+
*.suo
|
|
31
|
+
*.ntvs*
|
|
32
|
+
*.njsproj
|
|
33
|
+
*.sln
|
|
34
|
+
*.sw?
|
|
35
|
+
venv
|
|
36
|
+
__pycache__
|
|
37
|
+
.aider*
|
|
38
|
+
|
|
39
|
+
traces/*
|
|
40
|
+
data/*
|
|
41
|
+
recordings/*
|
spaik_sdk-0.6.2/Makefile
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Makefile for Agent SDK Testing
|
|
2
|
+
|
|
3
|
+
.PHONY: test test-unit test-integration test-watch test-cov test-fast help clean lint lint-fix typecheck
|
|
4
|
+
|
|
5
|
+
# Default target
|
|
6
|
+
help:
|
|
7
|
+
@echo "Available targets:"
|
|
8
|
+
@echo " test - Run all tests"
|
|
9
|
+
@echo " test-unit - Run only unit tests"
|
|
10
|
+
@echo " test-unit-single - Run single unit test by pattern"
|
|
11
|
+
@echo " test-integration - Run only integration tests"
|
|
12
|
+
@echo " test-fast - Run fast tests (exclude slow tests)"
|
|
13
|
+
@echo " test-watch - Run tests in watch mode"
|
|
14
|
+
@echo " test-cov - Run tests with coverage report"
|
|
15
|
+
@echo " test-html - Run tests with HTML coverage report"
|
|
16
|
+
@echo " lint - Run linting checks"
|
|
17
|
+
@echo " lint-fix - Run linting with auto-fix"
|
|
18
|
+
@echo " typecheck - Run type checking with mypy"
|
|
19
|
+
@echo " clean - Clean test artifacts"
|
|
20
|
+
|
|
21
|
+
# Run all tests
|
|
22
|
+
test:
|
|
23
|
+
uv run python3 -m pytest tests/
|
|
24
|
+
|
|
25
|
+
# Run only unit tests
|
|
26
|
+
test-unit:
|
|
27
|
+
uv run python3 -m pytest tests/unit/ -m "unit" --no-cov
|
|
28
|
+
|
|
29
|
+
# Run single unit test by pattern
|
|
30
|
+
test-unit-single:
|
|
31
|
+
@if [ -z "$(PATTERN)" ]; then echo "Usage: make test-unit-single PATTERN=test_name_pattern"; exit 1; fi
|
|
32
|
+
LOG_LEVEL=DEBUG uv run python3 -m pytest tests/unit/ -m "unit" --no-cov -k "$(PATTERN)" -v
|
|
33
|
+
|
|
34
|
+
# Run only integration tests
|
|
35
|
+
test-integration:
|
|
36
|
+
uv run python3 -m pytest tests/integration/ -m "integration"
|
|
37
|
+
|
|
38
|
+
# Run fast tests (exclude slow ones)
|
|
39
|
+
test-fast:
|
|
40
|
+
uv run python3 -m pytest tests/ -m "not slow"
|
|
41
|
+
|
|
42
|
+
# Run tests in watch mode (requires pytest-watch)
|
|
43
|
+
test-watch:
|
|
44
|
+
uv run pytest-watch --clear
|
|
45
|
+
|
|
46
|
+
# Run tests with coverage
|
|
47
|
+
test-cov:
|
|
48
|
+
uv run python3 -m pytest --cov=spaik_sdk --cov-report=term-missing
|
|
49
|
+
|
|
50
|
+
# Run tests with HTML coverage report
|
|
51
|
+
test-html:
|
|
52
|
+
uv run python3 -m pytest --cov=spaik_sdk --cov-report=html
|
|
53
|
+
@echo "Coverage report generated in htmlcov/index.html"
|
|
54
|
+
|
|
55
|
+
# Run specific test file
|
|
56
|
+
test-file:
|
|
57
|
+
@if [ -z "$(FILE)" ]; then echo "Usage: make test-file FILE=path/to/test_file.py"; exit 1; fi
|
|
58
|
+
uv run python3 -m pytest $(FILE) -v
|
|
59
|
+
|
|
60
|
+
# Run tests matching a pattern
|
|
61
|
+
test-pattern:
|
|
62
|
+
@if [ -z "$(PATTERN)" ]; then echo "Usage: make test-pattern PATTERN=test_name_pattern"; exit 1; fi
|
|
63
|
+
uv run python3 -m pytest -k "$(PATTERN)" -v
|
|
64
|
+
|
|
65
|
+
# Run linting checks
|
|
66
|
+
lint:
|
|
67
|
+
uv run ruff check .
|
|
68
|
+
|
|
69
|
+
# Run linting with auto-fix
|
|
70
|
+
lint-fix:
|
|
71
|
+
uv run ruff check --fix --unsafe-fixes .
|
|
72
|
+
uv run ruff format .
|
|
73
|
+
|
|
74
|
+
# Run type checking
|
|
75
|
+
typecheck:
|
|
76
|
+
uv run ty check spaik_sdk tests
|
|
77
|
+
|
|
78
|
+
# Clean test artifacts
|
|
79
|
+
clean:
|
|
80
|
+
rm -rf .pytest_cache/
|
|
81
|
+
rm -rf htmlcov/
|
|
82
|
+
rm -rf .coverage
|
|
83
|
+
find . -type d -name __pycache__ -exec rm -rf {} +
|
|
84
|
+
find . -type f -name "*.pyc" -delete
|
|
85
|
+
|
|
86
|
+
# Debug tests (run with verbose output and no capture)
|
|
87
|
+
test-debug:
|
|
88
|
+
uv run python3 -m pytest tests/ -v -s --tb=long
|
|
89
|
+
|
|
90
|
+
# Run tests and generate JUnit XML (for CI)
|
|
91
|
+
test-ci:
|
|
92
|
+
uv run python3 -m pytest tests/ --junitxml=test-results.xml --cov=spaik_sdk --cov-report=xml
|
spaik_sdk-0.6.2/PKG-INFO
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: spaik-sdk
|
|
3
|
+
Version: 0.6.2
|
|
4
|
+
Summary: Python SDK for building AI agents with multi-LLM support, streaming, and production-ready infrastructure
|
|
5
|
+
Project-URL: Homepage, https://github.com/siilisolutions/spaik-sdk
|
|
6
|
+
Project-URL: Repository, https://github.com/siilisolutions/spaik-sdk
|
|
7
|
+
Project-URL: Documentation, https://github.com/siilisolutions/spaik-sdk#readme
|
|
8
|
+
Author-email: Siili Solutions Oyj <info@siili.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Keywords: agents,ai,anthropic,claude,gpt,langchain,llm,openai,streaming
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: aioconsole>=0.8.1
|
|
21
|
+
Requires-Dist: azure-storage-blob
|
|
22
|
+
Requires-Dist: cryptography>=41.0.0
|
|
23
|
+
Requires-Dist: dotenv>=0.9.9
|
|
24
|
+
Requires-Dist: fastapi>=0.115.12
|
|
25
|
+
Requires-Dist: httpx>=0.25.0
|
|
26
|
+
Requires-Dist: langchain-anthropic>=1.3.0
|
|
27
|
+
Requires-Dist: langchain-core>=1.2.0
|
|
28
|
+
Requires-Dist: langchain-google-genai>=4.0.0
|
|
29
|
+
Requires-Dist: langchain-mcp-adapters>=0.2.1
|
|
30
|
+
Requires-Dist: langchain-ollama>=0.3.0
|
|
31
|
+
Requires-Dist: langchain-openai>=1.1.0
|
|
32
|
+
Requires-Dist: langchain-tavily>=0.2.15
|
|
33
|
+
Requires-Dist: langchain>=1.2.0
|
|
34
|
+
Requires-Dist: langgraph>=1.0.0
|
|
35
|
+
Requires-Dist: mcp>=1.9.2
|
|
36
|
+
Requires-Dist: pandas-stubs
|
|
37
|
+
Requires-Dist: pandas>=2.0.3
|
|
38
|
+
Requires-Dist: pyjwt>=2.8.0
|
|
39
|
+
Requires-Dist: pytest-asyncio>=0.21.1
|
|
40
|
+
Requires-Dist: pytest-cov>=4.1.0
|
|
41
|
+
Requires-Dist: pytest-mock>=3.11.1
|
|
42
|
+
Requires-Dist: pytest>=7.4.0
|
|
43
|
+
Requires-Dist: requests>=2.31.0
|
|
44
|
+
Requires-Dist: restrictedpython>=8.0
|
|
45
|
+
Requires-Dist: rich>=14.0.0
|
|
46
|
+
Requires-Dist: uvicorn>=0.33.0
|
|
47
|
+
Provides-Extra: dev
|
|
48
|
+
Requires-Dist: black; extra == 'dev'
|
|
49
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
50
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
51
|
+
Description-Content-Type: text/markdown
|
|
52
|
+
|
|
53
|
+
# Spaik SDK
|
|
54
|
+
|
|
55
|
+
Python SDK for building AI agents with multi-LLM support, streaming, and production infrastructure.
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install spaik-sdk
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from spaik_sdk.agent.base_agent import BaseAgent
|
|
67
|
+
|
|
68
|
+
class MyAgent(BaseAgent):
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
agent = MyAgent(system_prompt="You are a helpful assistant.")
|
|
72
|
+
print(agent.get_response_text("Hello!"))
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Features
|
|
76
|
+
|
|
77
|
+
- **Multi-LLM Support**: OpenAI, Anthropic, Google, Azure, Ollama
|
|
78
|
+
- **Unified API**: Same interface across all providers
|
|
79
|
+
- **Streaming**: Real-time response streaming via SSE
|
|
80
|
+
- **Tools**: Function calling with LangChain integration
|
|
81
|
+
- **Structured Output**: Pydantic model responses
|
|
82
|
+
- **Server**: FastAPI with thread persistence, auth, file uploads
|
|
83
|
+
- **Audio**: Text-to-speech and speech-to-text
|
|
84
|
+
- **Cost Tracking**: Token usage and cost estimation
|
|
85
|
+
|
|
86
|
+
## Agent API
|
|
87
|
+
|
|
88
|
+
### Basic Response Methods
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from spaik_sdk.agent.base_agent import BaseAgent
|
|
92
|
+
from spaik_sdk.models.model_registry import ModelRegistry
|
|
93
|
+
|
|
94
|
+
agent = MyAgent(
|
|
95
|
+
system_prompt="You are helpful.",
|
|
96
|
+
llm_model=ModelRegistry.CLAUDE_4_SONNET
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Sync - text only
|
|
100
|
+
text = agent.get_response_text("Hello")
|
|
101
|
+
|
|
102
|
+
# Sync - full message with blocks
|
|
103
|
+
message = agent.get_response("Hello")
|
|
104
|
+
print(message.get_text_content())
|
|
105
|
+
|
|
106
|
+
# Async
|
|
107
|
+
message = await agent.get_response_async("Hello")
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Streaming
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
# Token stream
|
|
114
|
+
async for chunk in agent.get_response_stream("Write a story"):
|
|
115
|
+
print(chunk, end="", flush=True)
|
|
116
|
+
|
|
117
|
+
# Event stream (for SSE)
|
|
118
|
+
async for event in agent.get_event_stream("Write a story"):
|
|
119
|
+
if event.get_event_type() == "StreamingUpdated":
|
|
120
|
+
print(event.content, end="")
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Structured Output
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
from pydantic import BaseModel
|
|
127
|
+
|
|
128
|
+
class Recipe(BaseModel):
|
|
129
|
+
name: str
|
|
130
|
+
ingredients: list[str]
|
|
131
|
+
steps: list[str]
|
|
132
|
+
|
|
133
|
+
recipe = agent.get_structured_response("Give me a pasta recipe", Recipe)
|
|
134
|
+
print(recipe.name)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Interactive CLI
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
agent.run_cli() # Starts interactive chat in terminal
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Tools
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
from spaik_sdk.tools.tool_provider import ToolProvider, BaseTool, tool
|
|
147
|
+
|
|
148
|
+
class WeatherTools(ToolProvider):
|
|
149
|
+
def get_tools(self) -> list[BaseTool]:
|
|
150
|
+
@tool
|
|
151
|
+
def get_weather(city: str) -> str:
|
|
152
|
+
"""Get current weather for a city."""
|
|
153
|
+
return f"Sunny, 22°C in {city}"
|
|
154
|
+
|
|
155
|
+
@tool
|
|
156
|
+
def get_forecast(city: str, days: int = 3) -> str:
|
|
157
|
+
"""Get weather forecast."""
|
|
158
|
+
return f"{days}-day forecast for {city}: Sunny"
|
|
159
|
+
|
|
160
|
+
return [get_weather, get_forecast]
|
|
161
|
+
|
|
162
|
+
class WeatherAgent(BaseAgent):
|
|
163
|
+
def get_tool_providers(self) -> list[ToolProvider]:
|
|
164
|
+
return [WeatherTools()]
|
|
165
|
+
|
|
166
|
+
agent = WeatherAgent(system_prompt="You provide weather info.")
|
|
167
|
+
print(agent.get_response_text("What's the weather in Tokyo?"))
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Built-in Tool Providers
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
from spaik_sdk.tools.impl.search_tool_provider import SearchToolProvider
|
|
174
|
+
from spaik_sdk.tools.impl.mcp_tool_provider import MCPToolProvider
|
|
175
|
+
|
|
176
|
+
class MyAgent(BaseAgent):
|
|
177
|
+
def get_tool_providers(self):
|
|
178
|
+
return [
|
|
179
|
+
SearchToolProvider(), # Web search (Tavily)
|
|
180
|
+
MCPToolProvider(server), # MCP server tools
|
|
181
|
+
]
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Models
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from spaik_sdk.models.model_registry import ModelRegistry
|
|
188
|
+
|
|
189
|
+
# Anthropic
|
|
190
|
+
ModelRegistry.CLAUDE_4_SONNET
|
|
191
|
+
ModelRegistry.CLAUDE_4_OPUS
|
|
192
|
+
ModelRegistry.CLAUDE_4_5_SONNET
|
|
193
|
+
ModelRegistry.CLAUDE_4_5_OPUS
|
|
194
|
+
|
|
195
|
+
# OpenAI
|
|
196
|
+
ModelRegistry.GPT_4_1
|
|
197
|
+
ModelRegistry.GPT_4O
|
|
198
|
+
ModelRegistry.O4_MINI
|
|
199
|
+
|
|
200
|
+
# Google
|
|
201
|
+
ModelRegistry.GEMINI_2_5_FLASH
|
|
202
|
+
ModelRegistry.GEMINI_2_5_PRO
|
|
203
|
+
|
|
204
|
+
# Aliases
|
|
205
|
+
ModelRegistry.from_name("sonnet") # CLAUDE_4_SONNET
|
|
206
|
+
ModelRegistry.from_name("gpt 4.1") # GPT_4_1
|
|
207
|
+
ModelRegistry.from_name("gemini 2.5") # GEMINI_2_5_FLASH
|
|
208
|
+
|
|
209
|
+
# Custom model
|
|
210
|
+
from spaik_sdk.models.llm_model import LLMModel
|
|
211
|
+
from spaik_sdk.models.llm_families import LLMFamilies
|
|
212
|
+
|
|
213
|
+
custom = LLMModel(
|
|
214
|
+
family=LLMFamilies.OPENAI,
|
|
215
|
+
name="gpt-4-custom",
|
|
216
|
+
reasoning=False
|
|
217
|
+
)
|
|
218
|
+
ModelRegistry.register_custom(custom)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## FastAPI Server
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
from contextlib import asynccontextmanager
|
|
225
|
+
from fastapi import FastAPI
|
|
226
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
227
|
+
from spaik_sdk.agent.base_agent import BaseAgent
|
|
228
|
+
from spaik_sdk.server.api.routers.api_builder import ApiBuilder
|
|
229
|
+
|
|
230
|
+
class MyAgent(BaseAgent):
|
|
231
|
+
pass
|
|
232
|
+
|
|
233
|
+
@asynccontextmanager
|
|
234
|
+
async def lifespan(app: FastAPI):
|
|
235
|
+
agent = MyAgent(system_prompt="You are helpful.")
|
|
236
|
+
api_builder = ApiBuilder.local(agent=agent)
|
|
237
|
+
|
|
238
|
+
app.include_router(api_builder.build_thread_router())
|
|
239
|
+
app.include_router(api_builder.build_file_router())
|
|
240
|
+
app.include_router(api_builder.build_audio_router())
|
|
241
|
+
yield
|
|
242
|
+
|
|
243
|
+
app = FastAPI(lifespan=lifespan)
|
|
244
|
+
app.add_middleware(
|
|
245
|
+
CORSMiddleware,
|
|
246
|
+
allow_origins=["*"],
|
|
247
|
+
allow_methods=["*"],
|
|
248
|
+
allow_headers=["*"],
|
|
249
|
+
)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### API Endpoints
|
|
253
|
+
|
|
254
|
+
Thread management:
|
|
255
|
+
- `POST /threads` - Create thread
|
|
256
|
+
- `GET /threads` - List threads
|
|
257
|
+
- `GET /threads/{id}` - Get thread with messages
|
|
258
|
+
- `POST /threads/{id}/messages/stream` - Send message (SSE)
|
|
259
|
+
- `DELETE /threads/{id}` - Delete thread
|
|
260
|
+
- `POST /threads/{id}/cancel` - Cancel generation
|
|
261
|
+
|
|
262
|
+
Files:
|
|
263
|
+
- `POST /files` - Upload file
|
|
264
|
+
- `GET /files/{id}` - Download file
|
|
265
|
+
|
|
266
|
+
Audio:
|
|
267
|
+
- `POST /audio/speech` - Text to speech
|
|
268
|
+
- `POST /audio/transcribe` - Speech to text
|
|
269
|
+
|
|
270
|
+
### Production Setup
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
from spaik_sdk.server.storage.impl.local_file_thread_repository import LocalFileThreadRepository
|
|
274
|
+
from spaik_sdk.server.authorization.base_authorizer import BaseAuthorizer
|
|
275
|
+
|
|
276
|
+
# Custom repository and auth
|
|
277
|
+
api_builder = ApiBuilder.stateful(
|
|
278
|
+
repository=LocalFileThreadRepository(base_path="./data"),
|
|
279
|
+
authorizer=MyAuthorizer(),
|
|
280
|
+
agent=agent,
|
|
281
|
+
)
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Orchestration
|
|
285
|
+
|
|
286
|
+
Code-first workflow orchestration without graph DSLs:
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
from spaik_sdk.orchestration import BaseOrchestrator, OrchestratorEvent
|
|
290
|
+
from dataclasses import dataclass
|
|
291
|
+
from typing import AsyncIterator
|
|
292
|
+
|
|
293
|
+
@dataclass
|
|
294
|
+
class State:
|
|
295
|
+
items: list[str]
|
|
296
|
+
|
|
297
|
+
@dataclass
|
|
298
|
+
class Result:
|
|
299
|
+
count: int
|
|
300
|
+
|
|
301
|
+
class MyOrchestrator(BaseOrchestrator[State, Result]):
|
|
302
|
+
async def run(self) -> AsyncIterator[OrchestratorEvent[Result]]:
|
|
303
|
+
state = State(items=[])
|
|
304
|
+
|
|
305
|
+
# Run step with automatic status events
|
|
306
|
+
async for event in self.step("fetch", "Fetching data", self.fetch, state):
|
|
307
|
+
yield event
|
|
308
|
+
if event.result:
|
|
309
|
+
state = event.result
|
|
310
|
+
|
|
311
|
+
# Progress updates
|
|
312
|
+
for i, item in enumerate(state.items):
|
|
313
|
+
yield self.progress("process", i + 1, len(state.items))
|
|
314
|
+
await self.process(item)
|
|
315
|
+
|
|
316
|
+
yield self.ok(Result(count=len(state.items)))
|
|
317
|
+
|
|
318
|
+
async def fetch(self, state: State) -> State:
|
|
319
|
+
return State(items=["a", "b", "c"])
|
|
320
|
+
|
|
321
|
+
async def process(self, item: str):
|
|
322
|
+
pass
|
|
323
|
+
|
|
324
|
+
# Run
|
|
325
|
+
orchestrator = MyOrchestrator()
|
|
326
|
+
result = orchestrator.run_sync()
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Configuration
|
|
330
|
+
|
|
331
|
+
Environment variables:
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
# LLM Providers (at least one required)
|
|
335
|
+
ANTHROPIC_API_KEY=sk-ant-...
|
|
336
|
+
OPENAI_API_KEY=sk-...
|
|
337
|
+
GOOGLE_API_KEY=...
|
|
338
|
+
|
|
339
|
+
# Optional
|
|
340
|
+
AZURE_API_KEY=...
|
|
341
|
+
AZURE_ENDPOINT=https://your-resource.openai.azure.com/
|
|
342
|
+
DEFAULT_MODEL=claude-sonnet-4-20250514
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## Development
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
# Setup
|
|
349
|
+
uv sync
|
|
350
|
+
|
|
351
|
+
# Tests
|
|
352
|
+
make test # All
|
|
353
|
+
make test-unit # Unit only
|
|
354
|
+
make test-integration # Integration only
|
|
355
|
+
make test-unit-single PATTERN=name # Single test
|
|
356
|
+
|
|
357
|
+
# Quality
|
|
358
|
+
make lint # Check linting
|
|
359
|
+
make lint-fix # Fix linting
|
|
360
|
+
make typecheck # Type check
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## Message Structure
|
|
364
|
+
|
|
365
|
+
Messages contain blocks of different types:
|
|
366
|
+
|
|
367
|
+
```python
|
|
368
|
+
from spaik_sdk.thread.models import MessageBlockType
|
|
369
|
+
|
|
370
|
+
# Block types
|
|
371
|
+
MessageBlockType.PLAIN # Regular text
|
|
372
|
+
MessageBlockType.REASONING # Chain of thought
|
|
373
|
+
MessageBlockType.TOOL_USE # Tool call
|
|
374
|
+
MessageBlockType.ERROR # Error message
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## License
|
|
378
|
+
|
|
379
|
+
MIT - Copyright (c) 2025 Siili Solutions Oyj
|