yera 0.1.0__py3-none-any.whl → 0.2.0__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 (192) hide show
  1. infra_mvp/base_client.py +29 -0
  2. infra_mvp/base_server.py +68 -0
  3. infra_mvp/monitoring/__init__.py +15 -0
  4. infra_mvp/monitoring/metrics.py +185 -0
  5. infra_mvp/stream/README.md +56 -0
  6. infra_mvp/stream/__init__.py +14 -0
  7. infra_mvp/stream/__main__.py +101 -0
  8. infra_mvp/stream/agents/demos/financial/chart_additions_plan.md +170 -0
  9. infra_mvp/stream/agents/demos/financial/portfolio_assistant_stream.json +1571 -0
  10. infra_mvp/stream/agents/reference/blocks/action.json +170 -0
  11. infra_mvp/stream/agents/reference/blocks/button.json +66 -0
  12. infra_mvp/stream/agents/reference/blocks/date.json +65 -0
  13. infra_mvp/stream/agents/reference/blocks/input_prompt.json +94 -0
  14. infra_mvp/stream/agents/reference/blocks/layout.json +288 -0
  15. infra_mvp/stream/agents/reference/blocks/markdown.json +344 -0
  16. infra_mvp/stream/agents/reference/blocks/slider.json +67 -0
  17. infra_mvp/stream/agents/reference/blocks/spinner.json +110 -0
  18. infra_mvp/stream/agents/reference/blocks/table.json +56 -0
  19. infra_mvp/stream/agents/reference/chat_dynamics/branching_test_stream.json +145 -0
  20. infra_mvp/stream/app.py +49 -0
  21. infra_mvp/stream/container.py +112 -0
  22. infra_mvp/stream/schemas/__init__.py +16 -0
  23. infra_mvp/stream/schemas/agent.py +24 -0
  24. infra_mvp/stream/schemas/interaction.py +28 -0
  25. infra_mvp/stream/schemas/session.py +30 -0
  26. infra_mvp/stream/server.py +321 -0
  27. infra_mvp/stream/services/__init__.py +12 -0
  28. infra_mvp/stream/services/agent_service.py +40 -0
  29. infra_mvp/stream/services/event_converter.py +83 -0
  30. infra_mvp/stream/services/session_service.py +247 -0
  31. yera/__init__.py +50 -1
  32. yera/agents/__init__.py +2 -0
  33. yera/agents/context.py +41 -0
  34. yera/agents/dataclasses.py +69 -0
  35. yera/agents/decorator.py +207 -0
  36. yera/agents/discovery.py +124 -0
  37. yera/agents/typing/__init__.py +0 -0
  38. yera/agents/typing/coerce.py +408 -0
  39. yera/agents/typing/utils.py +19 -0
  40. yera/agents/typing/validate.py +206 -0
  41. yera/cli.py +377 -0
  42. yera/config/__init__.py +1 -0
  43. yera/config/config_utils.py +164 -0
  44. yera/config/function_config.py +55 -0
  45. yera/config/logging.py +18 -0
  46. yera/config/tool_config.py +8 -0
  47. yera/config2/__init__.py +8 -0
  48. yera/config2/dataclasses.py +534 -0
  49. yera/config2/keyring.py +270 -0
  50. yera/config2/paths.py +28 -0
  51. yera/config2/read.py +113 -0
  52. yera/config2/setup.py +109 -0
  53. yera/config2/setup_handlers/__init__.py +1 -0
  54. yera/config2/setup_handlers/anthropic.py +126 -0
  55. yera/config2/setup_handlers/azure.py +236 -0
  56. yera/config2/setup_handlers/base.py +125 -0
  57. yera/config2/setup_handlers/llama_cpp.py +205 -0
  58. yera/config2/setup_handlers/ollama.py +157 -0
  59. yera/config2/setup_handlers/openai.py +137 -0
  60. yera/config2/write.py +87 -0
  61. yera/dsl/__init__.py +0 -0
  62. yera/dsl/functions.py +94 -0
  63. yera/dsl/struct.py +20 -0
  64. yera/dsl/workspace.py +79 -0
  65. yera/events/__init__.py +57 -0
  66. yera/events/blocks/__init__.py +68 -0
  67. yera/events/blocks/action.py +57 -0
  68. yera/events/blocks/bar_chart.py +92 -0
  69. yera/events/blocks/base/__init__.py +20 -0
  70. yera/events/blocks/base/base.py +166 -0
  71. yera/events/blocks/base/chart.py +288 -0
  72. yera/events/blocks/base/layout.py +111 -0
  73. yera/events/blocks/buttons.py +37 -0
  74. yera/events/blocks/columns.py +26 -0
  75. yera/events/blocks/container.py +24 -0
  76. yera/events/blocks/date_picker.py +50 -0
  77. yera/events/blocks/exit.py +39 -0
  78. yera/events/blocks/form.py +24 -0
  79. yera/events/blocks/input_echo.py +22 -0
  80. yera/events/blocks/input_request.py +31 -0
  81. yera/events/blocks/line_chart.py +97 -0
  82. yera/events/blocks/markdown.py +67 -0
  83. yera/events/blocks/slider.py +54 -0
  84. yera/events/blocks/spinner.py +55 -0
  85. yera/events/blocks/system_prompt.py +22 -0
  86. yera/events/blocks/table.py +291 -0
  87. yera/events/models/__init__.py +39 -0
  88. yera/events/models/block_data.py +112 -0
  89. yera/events/models/in_event.py +7 -0
  90. yera/events/models/out_event.py +75 -0
  91. yera/events/runtime.py +187 -0
  92. yera/events/stream.py +91 -0
  93. yera/models/__init__.py +0 -0
  94. yera/models/data_classes.py +20 -0
  95. yera/models/llm_atlas_proxy.py +44 -0
  96. yera/models/llm_context.py +99 -0
  97. yera/models/llm_interfaces/__init__.py +0 -0
  98. yera/models/llm_interfaces/anthropic.py +153 -0
  99. yera/models/llm_interfaces/aws_bedrock.py +14 -0
  100. yera/models/llm_interfaces/azure_openai.py +143 -0
  101. yera/models/llm_interfaces/base.py +26 -0
  102. yera/models/llm_interfaces/interface_registry.py +74 -0
  103. yera/models/llm_interfaces/llama_cpp.py +136 -0
  104. yera/models/llm_interfaces/mock.py +29 -0
  105. yera/models/llm_interfaces/ollama_interface.py +118 -0
  106. yera/models/llm_interfaces/open_ai.py +150 -0
  107. yera/models/llm_workspace.py +19 -0
  108. yera/models/model_atlas.py +139 -0
  109. yera/models/model_definition.py +38 -0
  110. yera/models/model_factory.py +33 -0
  111. yera/opaque/__init__.py +9 -0
  112. yera/opaque/base.py +20 -0
  113. yera/opaque/decorator.py +8 -0
  114. yera/opaque/markdown.py +57 -0
  115. yera/opaque/opaque_function.py +25 -0
  116. yera/tools/__init__.py +29 -0
  117. yera/tools/atlas_tool.py +20 -0
  118. yera/tools/base.py +24 -0
  119. yera/tools/decorated_tool.py +18 -0
  120. yera/tools/decorator.py +35 -0
  121. yera/tools/tool_atlas.py +51 -0
  122. yera/tools/tool_utils.py +361 -0
  123. yera/ui/dist/404.html +1 -0
  124. yera/ui/dist/__next.__PAGE__.txt +10 -0
  125. yera/ui/dist/__next._full.txt +23 -0
  126. yera/ui/dist/__next._head.txt +6 -0
  127. yera/ui/dist/__next._index.txt +5 -0
  128. yera/ui/dist/__next._tree.txt +7 -0
  129. yera/ui/dist/_next/static/chunks/4c4688e1ff21ad98.js +1 -0
  130. yera/ui/dist/_next/static/chunks/652cd53c27924d50.js +4 -0
  131. yera/ui/dist/_next/static/chunks/786d2107b51e8499.css +1 -0
  132. yera/ui/dist/_next/static/chunks/7de9141b1af425c3.js +1 -0
  133. yera/ui/dist/_next/static/chunks/87ef65064d3524c1.js +2 -0
  134. yera/ui/dist/_next/static/chunks/a6dad97d9634a72d.js +1 -0
  135. yera/ui/dist/_next/static/chunks/a6dad97d9634a72d.js.map +1 -0
  136. yera/ui/dist/_next/static/chunks/c4c79d5d0b280aeb.js +1 -0
  137. yera/ui/dist/_next/static/chunks/dc2d2a247505d66f.css +5 -0
  138. yera/ui/dist/_next/static/chunks/f773f714b55ec620.js +37 -0
  139. yera/ui/dist/_next/static/chunks/turbopack-98b3031e1b1dbc33.js +4 -0
  140. yera/ui/dist/_next/static/lnhYLzJ1-a5EfNbW1uFF6/_buildManifest.js +11 -0
  141. yera/ui/dist/_next/static/lnhYLzJ1-a5EfNbW1uFF6/_clientMiddlewareManifest.json +1 -0
  142. yera/ui/dist/_next/static/lnhYLzJ1-a5EfNbW1uFF6/_ssgManifest.js +1 -0
  143. yera/ui/dist/_next/static/media/14e23f9b59180572-s.9c448f3c.woff2 +0 -0
  144. yera/ui/dist/_next/static/media/2a65768255d6b625-s.p.d19752fb.woff2 +0 -0
  145. yera/ui/dist/_next/static/media/2b2eb4836d2dad95-s.f36de3af.woff2 +0 -0
  146. yera/ui/dist/_next/static/media/31183d9fd602dc89-s.c4ff9b73.woff2 +0 -0
  147. yera/ui/dist/_next/static/media/3fcb63a1ac6a562e-s.2f77a576.woff2 +0 -0
  148. yera/ui/dist/_next/static/media/45ec8de98929b0f6-s.81056204.woff2 +0 -0
  149. yera/ui/dist/_next/static/media/4fa387ec64143e14-s.c1fdd6c2.woff2 +0 -0
  150. yera/ui/dist/_next/static/media/65c558afe41e89d6-s.e2c8389a.woff2 +0 -0
  151. yera/ui/dist/_next/static/media/67add6cc0f54b8cf-s.8ce53448.woff2 +0 -0
  152. yera/ui/dist/_next/static/media/7178b3e590c64307-s.b97b3418.woff2 +0 -0
  153. yera/ui/dist/_next/static/media/797e433ab948586e-s.p.dbea232f.woff2 +0 -0
  154. yera/ui/dist/_next/static/media/8a480f0b521d4e75-s.8e0177b5.woff2 +0 -0
  155. yera/ui/dist/_next/static/media/a8ff2d5d0ccb0d12-s.fc5b72a7.woff2 +0 -0
  156. yera/ui/dist/_next/static/media/aae5f0be330e13db-s.p.853e26d6.woff2 +0 -0
  157. yera/ui/dist/_next/static/media/b11a6ccf4a3edec7-s.2113d282.woff2 +0 -0
  158. yera/ui/dist/_next/static/media/b49b0d9b851e4899-s.4f3fa681.woff2 +0 -0
  159. yera/ui/dist/_next/static/media/bbc41e54d2fcbd21-s.799d8ef8.woff2 +0 -0
  160. yera/ui/dist/_next/static/media/caa3a2e1cccd8315-s.p.853070df.woff2 +0 -0
  161. yera/ui/dist/_next/static/media/favicon.0b3bf435.ico +0 -0
  162. yera/ui/dist/_not-found/__next._full.txt +14 -0
  163. yera/ui/dist/_not-found/__next._head.txt +6 -0
  164. yera/ui/dist/_not-found/__next._index.txt +5 -0
  165. yera/ui/dist/_not-found/__next._not-found.__PAGE__.txt +5 -0
  166. yera/ui/dist/_not-found/__next._not-found.txt +4 -0
  167. yera/ui/dist/_not-found/__next._tree.txt +2 -0
  168. yera/ui/dist/_not-found.html +1 -0
  169. yera/ui/dist/_not-found.txt +14 -0
  170. yera/ui/dist/agent-icon.svg +3 -0
  171. yera/ui/dist/favicon.ico +0 -0
  172. yera/ui/dist/file.svg +1 -0
  173. yera/ui/dist/globe.svg +1 -0
  174. yera/ui/dist/index.html +1 -0
  175. yera/ui/dist/index.txt +23 -0
  176. yera/ui/dist/logo/full_logo.png +0 -0
  177. yera/ui/dist/logo/rune_logo.png +0 -0
  178. yera/ui/dist/logo/rune_logo_borderless.png +0 -0
  179. yera/ui/dist/logo/text_logo.png +0 -0
  180. yera/ui/dist/next.svg +1 -0
  181. yera/ui/dist/send.png +0 -0
  182. yera/ui/dist/send_single.png +0 -0
  183. yera/ui/dist/vercel.svg +1 -0
  184. yera/ui/dist/window.svg +1 -0
  185. yera/utils/__init__.py +1 -0
  186. yera/utils/path_utils.py +38 -0
  187. yera-0.2.0.dist-info/METADATA +65 -0
  188. yera-0.2.0.dist-info/RECORD +190 -0
  189. {yera-0.1.0.dist-info → yera-0.2.0.dist-info}/WHEEL +1 -1
  190. yera-0.2.0.dist-info/entry_points.txt +2 -0
  191. yera-0.1.0.dist-info/METADATA +0 -11
  192. yera-0.1.0.dist-info/RECORD +0 -4
@@ -0,0 +1,206 @@
1
+ import inspect
2
+ from datetime import date, datetime, time, timedelta
3
+ from decimal import Decimal
4
+ from enum import Enum
5
+ from types import UnionType
6
+ from typing import Any, Literal, Union, get_args, get_origin
7
+
8
+ from yera.agents.dataclasses import AgentMetadata
9
+ from yera.agents.typing.utils import get_type_name
10
+ from yera.dsl.struct import Struct
11
+
12
+
13
+ def is_enum_type(expected_type: Any) -> bool:
14
+ """Check if type is an Enum subclass."""
15
+ try:
16
+ return inspect.isclass(expected_type) and issubclass(expected_type, Enum)
17
+ except TypeError:
18
+ return False
19
+
20
+
21
+ def is_struct_type(expected_type: Any) -> bool:
22
+ """Check if type is a Struct subclass."""
23
+ try:
24
+ return inspect.isclass(expected_type) and issubclass(expected_type, Struct)
25
+ except TypeError:
26
+ return False
27
+
28
+
29
+ def is_dataframe_type(type_hint) -> bool:
30
+ """Check if type is pandas DataFrame without importing pandas."""
31
+ try:
32
+ module = getattr(type_hint, "__module__", None)
33
+ name = getattr(type_hint, "__name__", None)
34
+ return module == "pandas.core.frame" and name == "DataFrame"
35
+ except (AttributeError, TypeError):
36
+ return False
37
+
38
+
39
+ SIMPLE_ALLOWED_TYPES = {
40
+ type(None),
41
+ None,
42
+ int,
43
+ float,
44
+ str,
45
+ bool,
46
+ datetime,
47
+ date,
48
+ time,
49
+ timedelta,
50
+ Decimal,
51
+ }
52
+
53
+
54
+ def _validate_union(args):
55
+ """All union members must be allowed."""
56
+ return all(_is_allowed_type(arg) for arg in args)
57
+
58
+
59
+ def _validate_literal(args):
60
+ """Literal values must be primitives."""
61
+ return all(isinstance(arg, int | float | str | bool | type(None)) for arg in args)
62
+
63
+
64
+ def _validate_list(args):
65
+ """Must have element type."""
66
+ return bool(args) and _is_allowed_type(args[0])
67
+
68
+
69
+ def _validate_dict(args):
70
+ """Must have key and value types."""
71
+ if not args or len(args) != 2:
72
+ return False
73
+ return _is_allowed_type(args[0]) and _is_allowed_type(args[1])
74
+
75
+
76
+ def _validate_set(args):
77
+ """Must have element type."""
78
+ return bool(args) and _is_allowed_type(args[0])
79
+
80
+
81
+ GENERIC_VALIDATORS = {
82
+ Union: _validate_union,
83
+ UnionType: _validate_union,
84
+ Literal: _validate_literal,
85
+ list: _validate_list,
86
+ dict: _validate_dict,
87
+ set: _validate_set,
88
+ }
89
+
90
+
91
+ def _is_allowed_type(type_hint) -> bool:
92
+ """Check if a type hint is allowed in agent signatures."""
93
+ # None
94
+ if type_hint is None or type_hint is type(None):
95
+ return True
96
+
97
+ # Simple types
98
+ if type_hint in SIMPLE_ALLOWED_TYPES:
99
+ return True
100
+
101
+ # Struct/Enum types
102
+ if (
103
+ is_struct_type(type_hint)
104
+ or is_enum_type(type_hint)
105
+ or is_dataframe_type(type_hint)
106
+ ):
107
+ return True
108
+
109
+ # Generic types with args validation
110
+ origin = get_origin(type_hint)
111
+ validator = GENERIC_VALIDATORS.get(origin)
112
+ if validator:
113
+ return validator(get_args(type_hint))
114
+
115
+ return False
116
+
117
+
118
+ ERROR_HINTS = {
119
+ list: "collections must specify element type, e.g., list[int]",
120
+ dict: "collections must specify key and value types, e.g., dict[str, int]",
121
+ set: "collections must specify element type, e.g., set[int]",
122
+ }
123
+
124
+
125
+ def _get_type_error_detail(type_hint) -> str:
126
+ """Get helpful error message for invalid type."""
127
+ type_name = get_type_name(type_hint)
128
+ origin = get_origin(type_hint)
129
+
130
+ hint = ERROR_HINTS.get(origin)
131
+ if hint:
132
+ return f"{type_name} ({hint})"
133
+ return type_name
134
+
135
+
136
+ def _validate_parameters(signature, type_hints):
137
+ """Validate parameter type hints, return (missing, invalid)."""
138
+ missing = []
139
+ invalid = []
140
+
141
+ for param_name in signature.parameters:
142
+ if param_name not in type_hints:
143
+ missing.append(param_name)
144
+ elif not _is_allowed_type(type_hints[param_name]):
145
+ error_detail = _get_type_error_detail(type_hints[param_name])
146
+ invalid.append((param_name, error_detail))
147
+
148
+ return missing, invalid
149
+
150
+
151
+ def _validate_return_type(signature):
152
+ """Validate return type hint, return error tuple or None."""
153
+ return_annotation = signature.return_annotation
154
+ if return_annotation is inspect.Parameter.empty:
155
+ return_annotation = None # void is OK
156
+
157
+ if not _is_allowed_type(return_annotation):
158
+ error_detail = _get_type_error_detail(return_annotation)
159
+ return "function return", error_detail
160
+ return None
161
+
162
+
163
+ def validate_signature(signature, type_hints, identifier):
164
+ """Validate that all type hints in signature are allowed."""
165
+ # Validate parameters
166
+ missing_types, invalid_types = _validate_parameters(signature, type_hints)
167
+
168
+ # Validate return type
169
+ return_error = _validate_return_type(signature)
170
+ if return_error:
171
+ invalid_types.append(return_error)
172
+
173
+ # Report errors
174
+ if missing_types:
175
+ raise TypeError(
176
+ f"Parameters {missing_types} for agent '{identifier}' "
177
+ f"are missing type hints. All parameters must be type-hinted."
178
+ )
179
+
180
+ if invalid_types:
181
+ invalid_str = ",\n ".join([f"'{n}': {t}" for n, t in invalid_types])
182
+ raise TypeError(
183
+ f"Agent '{identifier}' has invalid type hints: \n {invalid_str}. "
184
+ f"\nOnly supported types are allowed (primitives, Struct, Enum, datetime "
185
+ f"types, Decimal, DataFrame, and typed collections like list[T], dict[K,V]"
186
+ f", set[T])."
187
+ )
188
+
189
+
190
+ def assert_value_is_return_type(
191
+ return_value: Any, return_cls: type, meta: AgentMetadata
192
+ ):
193
+ if return_cls is None or return_cls is type(None):
194
+ if return_value is not None:
195
+ raise RuntimeError(
196
+ f"Agent '{meta.identifier}' is marked as returning None, "
197
+ f"but returned: {return_value!r}"
198
+ )
199
+ return
200
+
201
+ if not isinstance(return_value, return_cls):
202
+ raise TypeError(
203
+ f"Agent '{meta.identifier}' expected to return "
204
+ f"{meta.return_type}, but returned "
205
+ f"{type(return_value).__name__}."
206
+ )
yera/cli.py ADDED
@@ -0,0 +1,377 @@
1
+ """Command-line interface for Yera."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import logging
7
+ import signal
8
+ import sys
9
+ import time
10
+ import webbrowser
11
+ from pathlib import Path
12
+ from typing import Literal
13
+
14
+ from infra_mvp.stream.container import create_stream_server
15
+ from yera import llm
16
+ from yera.agents.decorator import AgentFunctionWrapper
17
+ from yera.agents.discovery import discover_agents, load_agent
18
+ from yera.config.logging import setup_logging
19
+
20
+
21
+ def _register_shutdown_handler(server) -> None:
22
+ """Register signal handlers for graceful shutdown."""
23
+
24
+ def shutdown(_sig, _frame):
25
+ logging.info("Shutting down server...")
26
+ server.stop()
27
+ sys.exit(0)
28
+
29
+ signal.signal(signal.SIGINT, shutdown)
30
+ signal.signal(signal.SIGTERM, shutdown)
31
+
32
+
33
+ def _run_developement_environment(
34
+ agents: dict[str, AgentFunctionWrapper],
35
+ startup_agent_id: str | None = None,
36
+ port: int = 8991,
37
+ host: str = "127.0.0.1",
38
+ log_events: bool = False,
39
+ no_browser: bool = False,
40
+ dev_ui: bool = False,
41
+ ) -> None:
42
+ """Run a Yera development environment."""
43
+ server = create_stream_server(
44
+ agents=agents,
45
+ host=host,
46
+ port=port,
47
+ log_events=log_events,
48
+ dev_ui=dev_ui,
49
+ )
50
+
51
+ _register_shutdown_handler(server)
52
+
53
+ if dev_ui:
54
+ ui_url = "http://localhost:3000"
55
+ logging.info("Development mode: expecting UI dev server at %s", ui_url)
56
+ else:
57
+ ui_url = f"http://{host}:{port}"
58
+ logging.info("Serving bundled UI from the stream server")
59
+
60
+ if startup_agent_id:
61
+ ui_url += f"/agents/{startup_agent_id}"
62
+
63
+ logging.info(f"Starting Yera stream server on {server.url_base}")
64
+ if startup_agent_id:
65
+ logging.info(f"Agent ID: {startup_agent_id}")
66
+ logging.info("Press Ctrl+C to stop the server")
67
+
68
+ with server:
69
+ time.sleep(0.5) # Wait for server ready
70
+
71
+ if no_browser:
72
+ logging.info(f"Server ready. Access the UI at: {ui_url}")
73
+ else:
74
+ logging.info(f"Opening browser to: {ui_url}")
75
+ webbrowser.open(ui_url)
76
+
77
+ while True:
78
+ time.sleep(1)
79
+
80
+
81
+ def run_agent(
82
+ agent_path: str,
83
+ port: int = 8991,
84
+ host: str = "127.0.0.1",
85
+ agent_id: str | None = None,
86
+ log_events: bool = False,
87
+ no_browser: bool = False,
88
+ dev_ui: bool = False,
89
+ ) -> None:
90
+ """Run a Yera agent and view it in the UI.
91
+
92
+ Args:
93
+ agent_path: Path to the agent file containing an agent.
94
+ port: Server port number (default: 8991).
95
+ host: Server host address (default: 127.0.0.1).
96
+ agent_id: Optional agent id when multiple agents exist in a file.
97
+ log_events: Enable logging of events to file.
98
+ no_browser: If True, don't automatically open browser window.
99
+ dev_ui: If True, use Next.js dev server (localhost:3000) instead of bundled static UI.
100
+ """
101
+ try:
102
+ agent = load_agent(agent_path, agent_id)
103
+ except (FileNotFoundError, ValueError):
104
+ logging.exception("Error loading Python agent")
105
+ sys.exit(1)
106
+
107
+ _run_developement_environment(
108
+ agents=[agent],
109
+ startup_agent_id=agent.metadata.identifier,
110
+ port=port,
111
+ host=host,
112
+ log_events=log_events,
113
+ no_browser=no_browser,
114
+ dev_ui=dev_ui,
115
+ )
116
+
117
+
118
+ def run_dev(
119
+ working_directory: str | None = None,
120
+ port: int = 8991,
121
+ host: str = "127.0.0.1",
122
+ log_events: bool = False,
123
+ no_browser: bool = False,
124
+ dev_ui: bool = False,
125
+ ) -> None:
126
+ """Run a local Yera development environment.
127
+
128
+ Args:
129
+ working_directory: Directory to run the development environment in.
130
+ port: Server port number (default: 8991).
131
+ host: Server host address (default: 127.0.0.1).
132
+ log_events: Enable logging of events to file (default: False).
133
+ no_browser: If True, don't automatically open browser window (default: False).
134
+ dev_ui: Allow development UI to be served from the stream server.
135
+ """
136
+ working_directory = (
137
+ Path(working_directory) if working_directory is not None else Path.cwd()
138
+ )
139
+
140
+ agents = discover_agents(working_directory)
141
+
142
+ if not agents:
143
+ logging.error(f"No agents found in {working_directory}")
144
+ sys.exit(1)
145
+
146
+ _run_developement_environment(
147
+ agents=agents,
148
+ port=port,
149
+ host=host,
150
+ log_events=log_events,
151
+ no_browser=no_browser,
152
+ dev_ui=dev_ui,
153
+ )
154
+
155
+
156
+ loc_map = {
157
+ "user": "global",
158
+ "project": "local",
159
+ }
160
+ loc_paths = {
161
+ "user": Path.home() / ".config" / "yera" / "yera.toml",
162
+ "project": Path.cwd() / "yera.toml",
163
+ }
164
+
165
+
166
+ def run_setup(location: Literal["user", "project"], auto_import: bool):
167
+ from yera.config2.setup import set_default_llm, setup
168
+
169
+ if location not in loc_map:
170
+ valid = ", ".join(loc_map.keys())
171
+ raise ValueError(f"{location} is not a valid location. Choose from {valid}.")
172
+
173
+ print(f"""
174
+ Welcome to the Yera setup helper!
175
+
176
+ This script will do the following:
177
+ * find your existing provider credentials
178
+ * store them safely on your OS's secure keyring
179
+ * use them to find your available models.
180
+ * create or extend your yera.toml file with your available models.
181
+
182
+ Yera currently supports Anthropic, OpenAI, Azure, LlamaCpp, Ollama.
183
+
184
+ Note: Azure users may need to run `az login`
185
+
186
+ We're going to set up for your {location}-level configuration at
187
+ * {loc_paths[location]!s}
188
+
189
+ If you're happy to continue enter \"y\" below:""")
190
+ res = input(" > ")
191
+ if res != "y":
192
+ print("Response was not y. Exiting...")
193
+ return
194
+
195
+ setup(loc_map[location], auto_import)
196
+
197
+ set_default_llm(loc_map[location])
198
+
199
+
200
+ def run_show(to_show: Literal["llms"]):
201
+ valid = ["llms"]
202
+
203
+ if to_show == "llms":
204
+ print(llm._ensure_atlas())
205
+ else:
206
+ valid_str = ", ".join(valid)
207
+ raise ValueError(
208
+ f"Cannot show objects for {to_show}. Valid options are {valid_str}"
209
+ )
210
+
211
+
212
+ def parse_cli_args(
213
+ args: list[str] | None = None,
214
+ ) -> tuple[argparse.Namespace, argparse.ArgumentParser]:
215
+ """Parse CLI arguments.
216
+
217
+ Args:
218
+ args: Optional list of arguments to parse. If None, uses sys.argv[1:].
219
+
220
+ Returns:
221
+ Tuple of (parsed arguments namespace, parser instance).
222
+ """
223
+ parser = argparse.ArgumentParser(
224
+ description="Yera CLI - Run agentic apps and view them in the UI"
225
+ )
226
+ subparsers = parser.add_subparsers(dest="command", help="Available commands")
227
+
228
+ # Run subcommand
229
+ run_parser = subparsers.add_parser(
230
+ "run",
231
+ help="Run a Yera app and view it in the UI",
232
+ )
233
+ run_parser.add_argument(
234
+ "agent_path",
235
+ type=str,
236
+ help="Path to the Python agent file to run",
237
+ )
238
+ run_parser.add_argument(
239
+ "--agent-id",
240
+ type=str,
241
+ default=None,
242
+ help=(
243
+ "ID of the Python agent to run when multiple agents exist in a single file."
244
+ ),
245
+ )
246
+ run_parser.add_argument(
247
+ "--port",
248
+ type=int,
249
+ default=8991,
250
+ help="Server port number (default: 8991)",
251
+ )
252
+ run_parser.add_argument(
253
+ "--host",
254
+ type=str,
255
+ default="127.0.0.1",
256
+ help="Server host address (default: 127.0.0.1)",
257
+ )
258
+ run_parser.add_argument(
259
+ "--log-events",
260
+ action="store_true",
261
+ help="Enable logging of events to file (logs to yera-event-log-<timestamp>.jsonl)",
262
+ )
263
+ run_parser.add_argument(
264
+ "--no-browser",
265
+ action="store_true",
266
+ help="Don't automatically open browser window",
267
+ )
268
+ run_parser.add_argument(
269
+ "--dev-ui",
270
+ action="store_true",
271
+ help="Use Next.js dev server (localhost:3000) instead of bundled static UI",
272
+ )
273
+
274
+ # Dev subcommand
275
+ dev_parser = subparsers.add_parser(
276
+ "dev",
277
+ help="Run a local Yera development environment",
278
+ )
279
+ dev_parser.add_argument(
280
+ "--dir",
281
+ type=str,
282
+ default=None,
283
+ help="Directory to run the development environment in",
284
+ )
285
+ dev_parser.add_argument(
286
+ "--port",
287
+ type=int,
288
+ default=8991,
289
+ help="Server port number (default: 8991)",
290
+ )
291
+ dev_parser.add_argument(
292
+ "--host",
293
+ type=str,
294
+ default="127.0.0.1",
295
+ help="Server host address (default: 127.0.0.1)",
296
+ )
297
+ dev_parser.add_argument(
298
+ "--log-events",
299
+ action="store_true",
300
+ help="Enable logging of events to file (logs to yera-event-log-<timestamp>.jsonl)",
301
+ )
302
+ dev_parser.add_argument(
303
+ "--no-browser",
304
+ action="store_true",
305
+ help="Don't automatically open browser window",
306
+ )
307
+ dev_parser.add_argument(
308
+ "--dev-ui",
309
+ action="store_true",
310
+ help="Use Next.js dev server (localhost:3000) instead of bundled static UI",
311
+ )
312
+
313
+ setup_parser = subparsers.add_parser("setup", help="Run Yera setup helper")
314
+ setup_parser.add_argument(
315
+ "--location",
316
+ type=str,
317
+ default="user",
318
+ help="""Where to build or update your Yera config at. Can be user or project:
319
+ * user: ~/.config/yera/yera.toml
320
+ * project: ./yera.toml
321
+ """,
322
+ )
323
+
324
+ show_parser = subparsers.add_parser("show", help="Print useful info to the shell")
325
+ show_parser.add_argument(
326
+ "object_type",
327
+ type=str,
328
+ help="""What type of object to show. LLMs only for now""",
329
+ )
330
+
331
+ if args is None:
332
+ parsed_args = parser.parse_args()
333
+ else:
334
+ parsed_args = parser.parse_args(args)
335
+
336
+ return parsed_args, parser
337
+
338
+
339
+ def main() -> None:
340
+ """Main entry point for yera CLI."""
341
+ setup_logging()
342
+
343
+ args, parser = parse_cli_args()
344
+
345
+ match args.command:
346
+ case "run":
347
+ run_agent(
348
+ agent_path=args.agent_path,
349
+ port=args.port,
350
+ host=args.host,
351
+ agent_id=args.agent_id,
352
+ log_events=args.log_events,
353
+ no_browser=args.no_browser,
354
+ dev_ui=args.dev_ui,
355
+ )
356
+ case "dev":
357
+ run_dev(
358
+ working_directory=args.dir,
359
+ port=args.port,
360
+ host=args.host,
361
+ log_events=args.log_events,
362
+ no_browser=args.no_browser,
363
+ dev_ui=args.dev_ui,
364
+ )
365
+ case "setup":
366
+ run_setup(args.location, True)
367
+ case "show":
368
+ run_show(args.object_type.lower())
369
+ case _:
370
+ parser.print_help()
371
+
372
+ # logging.info("Exiting Yera CLI")
373
+ sys.exit(0)
374
+
375
+
376
+ if __name__ == "__main__":
377
+ main()
@@ -0,0 +1 @@
1
+ """Configuration utilities for Yera."""