tactus 0.31.2__py3-none-any.whl

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.
Files changed (160) hide show
  1. tactus/__init__.py +49 -0
  2. tactus/adapters/__init__.py +9 -0
  3. tactus/adapters/broker_log.py +76 -0
  4. tactus/adapters/cli_hitl.py +189 -0
  5. tactus/adapters/cli_log.py +223 -0
  6. tactus/adapters/cost_collector_log.py +56 -0
  7. tactus/adapters/file_storage.py +367 -0
  8. tactus/adapters/http_callback_log.py +109 -0
  9. tactus/adapters/ide_log.py +71 -0
  10. tactus/adapters/lua_tools.py +336 -0
  11. tactus/adapters/mcp.py +289 -0
  12. tactus/adapters/mcp_manager.py +196 -0
  13. tactus/adapters/memory.py +53 -0
  14. tactus/adapters/plugins.py +419 -0
  15. tactus/backends/http_backend.py +58 -0
  16. tactus/backends/model_backend.py +35 -0
  17. tactus/backends/pytorch_backend.py +110 -0
  18. tactus/broker/__init__.py +12 -0
  19. tactus/broker/client.py +247 -0
  20. tactus/broker/protocol.py +183 -0
  21. tactus/broker/server.py +1123 -0
  22. tactus/broker/stdio.py +12 -0
  23. tactus/cli/__init__.py +7 -0
  24. tactus/cli/app.py +2245 -0
  25. tactus/cli/commands/__init__.py +0 -0
  26. tactus/core/__init__.py +32 -0
  27. tactus/core/config_manager.py +790 -0
  28. tactus/core/dependencies/__init__.py +14 -0
  29. tactus/core/dependencies/registry.py +180 -0
  30. tactus/core/dsl_stubs.py +2117 -0
  31. tactus/core/exceptions.py +66 -0
  32. tactus/core/execution_context.py +480 -0
  33. tactus/core/lua_sandbox.py +508 -0
  34. tactus/core/message_history_manager.py +236 -0
  35. tactus/core/mocking.py +286 -0
  36. tactus/core/output_validator.py +291 -0
  37. tactus/core/registry.py +499 -0
  38. tactus/core/runtime.py +2907 -0
  39. tactus/core/template_resolver.py +142 -0
  40. tactus/core/yaml_parser.py +301 -0
  41. tactus/docker/Dockerfile +61 -0
  42. tactus/docker/entrypoint.sh +69 -0
  43. tactus/dspy/__init__.py +39 -0
  44. tactus/dspy/agent.py +1144 -0
  45. tactus/dspy/broker_lm.py +181 -0
  46. tactus/dspy/config.py +212 -0
  47. tactus/dspy/history.py +196 -0
  48. tactus/dspy/module.py +405 -0
  49. tactus/dspy/prediction.py +318 -0
  50. tactus/dspy/signature.py +185 -0
  51. tactus/formatting/__init__.py +7 -0
  52. tactus/formatting/formatter.py +437 -0
  53. tactus/ide/__init__.py +9 -0
  54. tactus/ide/coding_assistant.py +343 -0
  55. tactus/ide/server.py +2223 -0
  56. tactus/primitives/__init__.py +49 -0
  57. tactus/primitives/control.py +168 -0
  58. tactus/primitives/file.py +229 -0
  59. tactus/primitives/handles.py +378 -0
  60. tactus/primitives/host.py +94 -0
  61. tactus/primitives/human.py +342 -0
  62. tactus/primitives/json.py +189 -0
  63. tactus/primitives/log.py +187 -0
  64. tactus/primitives/message_history.py +157 -0
  65. tactus/primitives/model.py +163 -0
  66. tactus/primitives/procedure.py +564 -0
  67. tactus/primitives/procedure_callable.py +318 -0
  68. tactus/primitives/retry.py +155 -0
  69. tactus/primitives/session.py +152 -0
  70. tactus/primitives/state.py +182 -0
  71. tactus/primitives/step.py +209 -0
  72. tactus/primitives/system.py +93 -0
  73. tactus/primitives/tool.py +375 -0
  74. tactus/primitives/tool_handle.py +279 -0
  75. tactus/primitives/toolset.py +229 -0
  76. tactus/protocols/__init__.py +38 -0
  77. tactus/protocols/chat_recorder.py +81 -0
  78. tactus/protocols/config.py +97 -0
  79. tactus/protocols/cost.py +31 -0
  80. tactus/protocols/hitl.py +71 -0
  81. tactus/protocols/log_handler.py +27 -0
  82. tactus/protocols/models.py +355 -0
  83. tactus/protocols/result.py +33 -0
  84. tactus/protocols/storage.py +90 -0
  85. tactus/providers/__init__.py +13 -0
  86. tactus/providers/base.py +92 -0
  87. tactus/providers/bedrock.py +117 -0
  88. tactus/providers/google.py +105 -0
  89. tactus/providers/openai.py +98 -0
  90. tactus/sandbox/__init__.py +63 -0
  91. tactus/sandbox/config.py +171 -0
  92. tactus/sandbox/container_runner.py +1099 -0
  93. tactus/sandbox/docker_manager.py +433 -0
  94. tactus/sandbox/entrypoint.py +227 -0
  95. tactus/sandbox/protocol.py +213 -0
  96. tactus/stdlib/__init__.py +10 -0
  97. tactus/stdlib/io/__init__.py +13 -0
  98. tactus/stdlib/io/csv.py +88 -0
  99. tactus/stdlib/io/excel.py +136 -0
  100. tactus/stdlib/io/file.py +90 -0
  101. tactus/stdlib/io/fs.py +154 -0
  102. tactus/stdlib/io/hdf5.py +121 -0
  103. tactus/stdlib/io/json.py +109 -0
  104. tactus/stdlib/io/parquet.py +83 -0
  105. tactus/stdlib/io/tsv.py +88 -0
  106. tactus/stdlib/loader.py +274 -0
  107. tactus/stdlib/tac/tactus/tools/done.tac +33 -0
  108. tactus/stdlib/tac/tactus/tools/log.tac +50 -0
  109. tactus/testing/README.md +273 -0
  110. tactus/testing/__init__.py +61 -0
  111. tactus/testing/behave_integration.py +380 -0
  112. tactus/testing/context.py +486 -0
  113. tactus/testing/eval_models.py +114 -0
  114. tactus/testing/evaluation_runner.py +222 -0
  115. tactus/testing/evaluators.py +634 -0
  116. tactus/testing/events.py +94 -0
  117. tactus/testing/gherkin_parser.py +134 -0
  118. tactus/testing/mock_agent.py +315 -0
  119. tactus/testing/mock_dependencies.py +234 -0
  120. tactus/testing/mock_hitl.py +171 -0
  121. tactus/testing/mock_registry.py +168 -0
  122. tactus/testing/mock_tools.py +133 -0
  123. tactus/testing/models.py +115 -0
  124. tactus/testing/pydantic_eval_runner.py +508 -0
  125. tactus/testing/steps/__init__.py +13 -0
  126. tactus/testing/steps/builtin.py +902 -0
  127. tactus/testing/steps/custom.py +69 -0
  128. tactus/testing/steps/registry.py +68 -0
  129. tactus/testing/test_runner.py +489 -0
  130. tactus/tracing/__init__.py +5 -0
  131. tactus/tracing/trace_manager.py +417 -0
  132. tactus/utils/__init__.py +1 -0
  133. tactus/utils/cost_calculator.py +72 -0
  134. tactus/utils/model_pricing.py +132 -0
  135. tactus/utils/safe_file_library.py +502 -0
  136. tactus/utils/safe_libraries.py +234 -0
  137. tactus/validation/LuaLexerBase.py +66 -0
  138. tactus/validation/LuaParserBase.py +23 -0
  139. tactus/validation/README.md +224 -0
  140. tactus/validation/__init__.py +7 -0
  141. tactus/validation/error_listener.py +21 -0
  142. tactus/validation/generated/LuaLexer.interp +231 -0
  143. tactus/validation/generated/LuaLexer.py +5548 -0
  144. tactus/validation/generated/LuaLexer.tokens +124 -0
  145. tactus/validation/generated/LuaLexerBase.py +66 -0
  146. tactus/validation/generated/LuaParser.interp +173 -0
  147. tactus/validation/generated/LuaParser.py +6439 -0
  148. tactus/validation/generated/LuaParser.tokens +124 -0
  149. tactus/validation/generated/LuaParserBase.py +23 -0
  150. tactus/validation/generated/LuaParserVisitor.py +118 -0
  151. tactus/validation/generated/__init__.py +7 -0
  152. tactus/validation/grammar/LuaLexer.g4 +123 -0
  153. tactus/validation/grammar/LuaParser.g4 +178 -0
  154. tactus/validation/semantic_visitor.py +817 -0
  155. tactus/validation/validator.py +157 -0
  156. tactus-0.31.2.dist-info/METADATA +1809 -0
  157. tactus-0.31.2.dist-info/RECORD +160 -0
  158. tactus-0.31.2.dist-info/WHEEL +4 -0
  159. tactus-0.31.2.dist-info/entry_points.txt +2 -0
  160. tactus-0.31.2.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,14 @@
1
+ """
2
+ Dependency injection infrastructure for Tactus procedures.
3
+
4
+ This package provides the infrastructure for declaring, creating, and managing
5
+ external dependencies like HTTP clients, databases, and caches.
6
+ """
7
+
8
+ from .registry import ResourceType, ResourceFactory, ResourceManager
9
+
10
+ __all__ = [
11
+ "ResourceType",
12
+ "ResourceFactory",
13
+ "ResourceManager",
14
+ ]
@@ -0,0 +1,180 @@
1
+ """
2
+ Resource registry for dependency injection in Tactus procedures.
3
+
4
+ This module provides the infrastructure for declaring, creating, and managing
5
+ external dependencies (HTTP clients, databases, caches) that procedures need.
6
+ """
7
+
8
+ from enum import Enum
9
+ from typing import Dict, Any
10
+ import logging
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class ResourceType(Enum):
16
+ """Supported dependency resource types."""
17
+
18
+ HTTP_CLIENT = "http_client"
19
+ POSTGRES = "postgres"
20
+ REDIS = "redis"
21
+
22
+
23
+ class ResourceFactory:
24
+ """
25
+ Factory for creating real dependency resources from configuration.
26
+
27
+ This factory creates actual HTTP clients, database connections, etc.
28
+ based on the configuration provided in procedure DSL.
29
+ """
30
+
31
+ @staticmethod
32
+ async def create(resource_type: str, config: Dict[str, Any]) -> Any:
33
+ """
34
+ Create a real resource from configuration.
35
+
36
+ Args:
37
+ resource_type: Type of resource (http_client, postgres, redis)
38
+ config: Configuration dictionary from procedure DSL
39
+
40
+ Returns:
41
+ Configured resource instance
42
+
43
+ Raises:
44
+ ValueError: If resource_type is unknown
45
+ ImportError: If required library is not installed
46
+ """
47
+ if resource_type == ResourceType.HTTP_CLIENT.value:
48
+ return await ResourceFactory._create_http_client(config)
49
+ elif resource_type == ResourceType.POSTGRES.value:
50
+ return await ResourceFactory._create_postgres(config)
51
+ elif resource_type == ResourceType.REDIS.value:
52
+ return await ResourceFactory._create_redis(config)
53
+ else:
54
+ raise ValueError(f"Unknown resource type: {resource_type}")
55
+
56
+ @staticmethod
57
+ async def _create_http_client(config: Dict[str, Any]) -> Any:
58
+ """Create HTTP client (httpx.AsyncClient)."""
59
+ try:
60
+ import httpx
61
+ except ImportError:
62
+ raise ImportError(
63
+ "httpx is required for HTTP client dependencies. Install it with: pip install httpx"
64
+ )
65
+
66
+ base_url = config.get("base_url")
67
+ headers = config.get("headers", {})
68
+ timeout = config.get("timeout", 30.0)
69
+
70
+ logger.info(f"Creating HTTP client for base_url={base_url}")
71
+
72
+ return httpx.AsyncClient(base_url=base_url, headers=headers, timeout=timeout)
73
+
74
+ @staticmethod
75
+ async def _create_postgres(config: Dict[str, Any]) -> Any:
76
+ """Create PostgreSQL connection pool (asyncpg.Pool)."""
77
+ try:
78
+ import asyncpg
79
+ except ImportError:
80
+ raise ImportError(
81
+ "asyncpg is required for PostgreSQL dependencies. "
82
+ "Install it with: pip install asyncpg"
83
+ )
84
+
85
+ connection_string = config["connection_string"]
86
+ pool_size = config.get("pool_size", 10)
87
+ max_pool_size = config.get("max_pool_size", 20)
88
+
89
+ logger.info(f"Creating PostgreSQL pool with size={pool_size}")
90
+
91
+ return await asyncpg.create_pool(
92
+ connection_string, min_size=pool_size, max_size=max_pool_size
93
+ )
94
+
95
+ @staticmethod
96
+ async def _create_redis(config: Dict[str, Any]) -> Any:
97
+ """Create Redis client (redis.asyncio.Redis)."""
98
+ try:
99
+ import redis.asyncio as redis
100
+ except ImportError:
101
+ raise ImportError(
102
+ "redis is required for Redis dependencies. Install it with: pip install redis"
103
+ )
104
+
105
+ url = config["url"]
106
+
107
+ logger.info(f"Creating Redis client for url={url}")
108
+
109
+ return redis.from_url(url, encoding="utf-8", decode_responses=True)
110
+
111
+ @staticmethod
112
+ async def create_all(dependencies_config: Dict[str, Dict[str, Any]]) -> Dict[str, Any]:
113
+ """
114
+ Create all dependencies from configuration.
115
+
116
+ Args:
117
+ dependencies_config: Dict mapping dependency name to config
118
+
119
+ Returns:
120
+ Dict mapping dependency name to created resource
121
+ """
122
+ resources = {}
123
+
124
+ for name, config in dependencies_config.items():
125
+ resource_type = config.get("type")
126
+ if not resource_type:
127
+ raise ValueError(f"Dependency '{name}' missing 'type' field")
128
+
129
+ logger.info(f"Creating dependency '{name}' of type '{resource_type}'")
130
+ resources[name] = await ResourceFactory.create(resource_type, config)
131
+
132
+ return resources
133
+
134
+
135
+ class ResourceManager:
136
+ """
137
+ Manages lifecycle of dependency resources.
138
+
139
+ Handles cleanup of HTTP connections, database pools, etc.
140
+ when procedure completes.
141
+ """
142
+
143
+ def __init__(self):
144
+ self.resources: Dict[str, Any] = {}
145
+
146
+ async def add_resource(self, name: str, resource: Any) -> None:
147
+ """Add a resource to be managed."""
148
+ self.resources[name] = resource
149
+ logger.debug(f"Added resource '{name}' to manager")
150
+
151
+ async def cleanup(self) -> None:
152
+ """Clean up all managed resources."""
153
+ logger.info(f"Cleaning up {len(self.resources)} resources")
154
+
155
+ for name, resource in self.resources.items():
156
+ try:
157
+ await self._cleanup_resource(name, resource)
158
+ except Exception as e:
159
+ logger.error(f"Error cleaning up resource '{name}': {e}")
160
+
161
+ async def _cleanup_resource(self, name: str, resource: Any) -> None:
162
+ """Clean up a single resource based on its type."""
163
+ # HTTP client cleanup
164
+ if hasattr(resource, "aclose"):
165
+ logger.debug(f"Closing HTTP client '{name}'")
166
+ await resource.aclose()
167
+
168
+ # PostgreSQL pool cleanup
169
+ elif hasattr(resource, "close") and hasattr(resource, "wait_closed"):
170
+ logger.debug(f"Closing PostgreSQL pool '{name}'")
171
+ await resource.close()
172
+ await resource.wait_closed()
173
+
174
+ # Redis client cleanup
175
+ elif hasattr(resource, "close") and not hasattr(resource, "wait_closed"):
176
+ logger.debug(f"Closing Redis client '{name}'")
177
+ await resource.close()
178
+
179
+ else:
180
+ logger.warning(f"Unknown resource type for '{name}', no cleanup performed")