agentic-programming 0.4.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.
agentic/__init__.py ADDED
@@ -0,0 +1,48 @@
1
+ """
2
+ Agentic Programming — Python functions that call LLMs with automatic context.
3
+
4
+ Three things:
5
+
6
+ @agentic_function Decorator. Records every call into a Context tree.
7
+ Runtime LLM runtime class. Handles context injection and recording.
8
+ Context The tree of execution records. Query it with summarize().
9
+
10
+ Quick start:
11
+
12
+ from agentic import agentic_function, Runtime
13
+
14
+ runtime = Runtime(call=my_llm_func, model="gpt-4o")
15
+
16
+ @agentic_function
17
+ def observe(task):
18
+ '''Look at the screen and describe what you see.'''
19
+ return runtime.exec(content=[
20
+ {"type": "text", "text": "Find the login button."},
21
+ {"type": "image", "path": "screenshot.png"},
22
+ ])
23
+
24
+ @agentic_function(compress=True)
25
+ def navigate(target):
26
+ '''Navigate to a target element.'''
27
+ obs = observe(f"find {target}")
28
+ action = plan(obs)
29
+ act(action)
30
+ return verify(target)
31
+ """
32
+
33
+ from agentic.context import Context
34
+ from agentic.function import agentic_function
35
+ from agentic.runtime import Runtime
36
+ from agentic.meta_functions import create, create_app, fix, create_skill
37
+ from agentic.providers import detect_provider, create_runtime
38
+
39
+ __all__ = [
40
+ "agentic_function",
41
+ "Runtime",
42
+ "Context",
43
+ "create",
44
+ "create_app",
45
+ "fix",
46
+ "detect_provider",
47
+ "create_runtime",
48
+ ]
@@ -0,0 +1 @@
1
+ # Generated apps — created by agentic.meta_functions.create_app()
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env python3
2
+ """A CLI tool that takes a topic and generates a mini-lesson: first identify 3 key concepts, then explain each one, then synthesize a takeaway. Generated by agentic create-app."""
3
+
4
+ import argparse
5
+ from agentic import agentic_function, create_runtime
6
+
7
+ runtime = create_runtime()
8
+
9
+ @agentic_function
10
+ def identify_key_concepts(topic: str) -> str:
11
+ """Identify 3 key concepts related to the topic."""
12
+ return runtime.exec(content=[
13
+ {"type": "text", "text": f"Identify 3 key concepts about {topic}."},
14
+ ])
15
+
16
+ @agentic_function
17
+ def explain_concepts(concepts: str) -> str:
18
+ """Explain each of the identified key concepts."""
19
+ return runtime.exec(content=[
20
+ {"type": "text", "text": f"Explain these concepts: {concepts}."},
21
+ ])
22
+
23
+ @agentic_function
24
+ def synthesize_takeaway(explanation: str) -> str:
25
+ """Synthesize a takeaway from the explanations."""
26
+ return runtime.exec(content=[
27
+ {"type": "text", "text": f"Synthesize a takeaway from these explanations: {explanation}."},
28
+ ])
29
+
30
+ def main():
31
+ parser = argparse.ArgumentParser(description="Generate a mini-lesson from a given topic.")
32
+ parser.add_argument("topic", help="The topic for the mini-lesson")
33
+ parser.add_argument("--provider", default=None, help="LLM provider override")
34
+ parser.add_argument("--model", default=None, help="Model override")
35
+ args = parser.parse_args()
36
+
37
+ global runtime
38
+ runtime = create_runtime(provider=args.provider, model=args.model)
39
+
40
+ concepts = identify_key_concepts(topic=args.topic)
41
+ explanation = explain_concepts(concepts=concepts)
42
+ takeaway = synthesize_takeaway(explanation=explanation)
43
+
44
+ print(takeaway)
45
+
46
+ if __name__ == "__main__":
47
+ main()
agentic/cli.py ADDED
@@ -0,0 +1,319 @@
1
+ """
2
+ agentic CLI — command-line interface for Agentic Programming.
3
+
4
+ Usage:
5
+ agentic create "description" --name my_func
6
+ agentic fix my_func --instruction "change X to Y"
7
+ agentic run my_func --arg key=value
8
+ agentic list
9
+ agentic create-skill my_func
10
+ agentic providers # show available providers
11
+ agentic create "desc" --provider anthropic --model claude-sonnet-4-20250514
12
+ """
13
+
14
+ import argparse
15
+ import sys
16
+ import json
17
+
18
+
19
+ def _add_provider_args(parser):
20
+ """Add --provider and --model arguments to a subcommand parser."""
21
+ parser.add_argument(
22
+ "--provider", "-p",
23
+ default=None,
24
+ help="LLM provider: claude-code, codex, gemini-cli, anthropic, openai, gemini. "
25
+ "Auto-detected if not specified.",
26
+ )
27
+ parser.add_argument(
28
+ "--model", "-m",
29
+ default=None,
30
+ help="Model name override (e.g. sonnet, gpt-4o, claude-sonnet-4-20250514).",
31
+ )
32
+
33
+
34
+ def main():
35
+ parser = argparse.ArgumentParser(
36
+ prog="agentic",
37
+ description="Agentic Programming CLI — create, fix, and run LLM-powered functions.",
38
+ )
39
+ sub = parser.add_subparsers(dest="command", help="Command to run")
40
+
41
+ # create
42
+ p_create = sub.add_parser("create", help="Create a new function from description")
43
+ p_create.add_argument("description", help="What the function should do")
44
+ p_create.add_argument("--name", "-n", required=True, help="Function name")
45
+ p_create.add_argument("--as-skill", action="store_true", help="Also create a SKILL.md")
46
+ _add_provider_args(p_create)
47
+
48
+ # fix
49
+ p_fix = sub.add_parser("fix", help="Fix an existing function")
50
+ p_fix.add_argument("name", help="Function name to fix")
51
+ p_fix.add_argument("--instruction", "-i", default=None, help="What to change")
52
+ _add_provider_args(p_fix)
53
+
54
+ # run
55
+ p_run = sub.add_parser("run", help="Run an existing function")
56
+ p_run.add_argument("name", help="Function name to run")
57
+ p_run.add_argument("--arg", "-a", action="append", default=[], help="Arguments as key=value")
58
+ _add_provider_args(p_run)
59
+
60
+ # list
61
+ sub.add_parser("list", help="List all saved functions")
62
+
63
+ # create-app
64
+ p_app = sub.add_parser("create-app", help="Create a complete runnable app (runtime + functions + main)")
65
+ p_app.add_argument("description", help="What the app should do")
66
+ p_app.add_argument("--name", "-n", default="app", help="App name (default: app)")
67
+ _add_provider_args(p_app)
68
+
69
+ # create-skill
70
+ p_skill = sub.add_parser("create-skill", help="Create a SKILL.md for a function")
71
+ p_skill.add_argument("name", help="Function name")
72
+ _add_provider_args(p_skill)
73
+
74
+ # providers
75
+ sub.add_parser("providers", help="Show available LLM providers and detection status")
76
+
77
+ args = parser.parse_args()
78
+
79
+ if args.command is None:
80
+ parser.print_help()
81
+ return
82
+
83
+ # Lazy imports — only load when needed
84
+ if args.command == "list":
85
+ _cmd_list()
86
+ elif args.command == "providers":
87
+ _cmd_providers()
88
+ elif args.command == "create":
89
+ _cmd_create(args.description, args.name, args.as_skill, args.provider, args.model)
90
+ elif args.command == "create-app":
91
+ _cmd_create_app(args.description, args.name, args.provider, args.model)
92
+ elif args.command == "fix":
93
+ _cmd_fix(args.name, args.instruction, args.provider, args.model)
94
+ elif args.command == "run":
95
+ _cmd_run(args.name, args.arg, args.provider, args.model)
96
+ elif args.command == "create-skill":
97
+ _cmd_create_skill(args.name, args.provider, args.model)
98
+
99
+
100
+ def _get_runtime(provider=None, model=None):
101
+ """Get a Runtime via auto-detection or explicit provider.
102
+
103
+ Args:
104
+ provider: Provider name (e.g. "anthropic", "claude-code").
105
+ If None, auto-detects the best available.
106
+ model: Model name override.
107
+
108
+ Returns:
109
+ A ready-to-use Runtime instance.
110
+ """
111
+ from agentic.providers import create_runtime
112
+ return create_runtime(provider=provider, model=model)
113
+
114
+
115
+ def _get_functions_dir():
116
+ import os
117
+ return os.path.join(os.path.dirname(__file__), "functions")
118
+
119
+
120
+ def _cmd_providers():
121
+ """Show available providers and which one would be auto-detected."""
122
+ import os
123
+ import shutil
124
+ from agentic.providers import PROVIDERS
125
+
126
+ print("Available LLM providers:\n")
127
+
128
+ # Check what's available
129
+ detected = None
130
+ statuses = {}
131
+
132
+ cli_checks = {
133
+ "claude-code": ("claude", "Claude Code CLI"),
134
+ "codex": ("codex", "Codex CLI"),
135
+ "gemini-cli": ("gemini", "Gemini CLI"),
136
+ }
137
+ api_checks = {
138
+ "anthropic": ("ANTHROPIC_API_KEY", "Anthropic API"),
139
+ "openai": ("OPENAI_API_KEY", "OpenAI API"),
140
+ "gemini": ("GOOGLE_API_KEY", "Gemini API"),
141
+ }
142
+
143
+ # Detection order matches detect_provider()
144
+ detection_order = ["claude-code", "codex", "gemini-cli", "anthropic", "openai", "gemini"]
145
+
146
+ for name in detection_order:
147
+ _, _, default_model = PROVIDERS[name]
148
+
149
+ if name in cli_checks:
150
+ cmd, label = cli_checks[name]
151
+ found = shutil.which(cmd) is not None
152
+ status = "ready" if found else "not found"
153
+ how = f"`{cmd}` in PATH" if found else f"install: npm install -g ..."
154
+ else:
155
+ env_var, label = api_checks[name]
156
+ found = bool(os.environ.get(env_var))
157
+ status = "ready" if found else "not set"
158
+ how = f"${env_var}" if found else f"export {env_var}=..."
159
+
160
+ if found and detected is None:
161
+ detected = name
162
+ marker = " <-- auto-detected"
163
+ else:
164
+ marker = ""
165
+
166
+ icon = "+" if found else "-"
167
+ print(f" [{icon}] {name:14s} ({label:16s}) model: {default_model:30s} [{status}]{marker}")
168
+
169
+ print()
170
+ if detected:
171
+ print(f"Auto-detected provider: {detected}")
172
+ print(f"Override with: agentic <command> --provider <name> --model <model>")
173
+ else:
174
+ print("No provider detected. Set up one of the above to get started.")
175
+ print("See: https://github.com/Fzkuji/Agentic-Programming#quick-start")
176
+
177
+
178
+ def _cmd_list():
179
+ """List all saved functions."""
180
+ import os
181
+ functions_dir = _get_functions_dir()
182
+ if not os.path.exists(functions_dir):
183
+ print("No functions created yet.")
184
+ return
185
+
186
+ files = [f[:-3] for f in os.listdir(functions_dir)
187
+ if f.endswith(".py") and f != "__init__.py"]
188
+ if not files:
189
+ print("No functions created yet.")
190
+ return
191
+
192
+ print(f"Functions ({len(files)}):\n")
193
+ for name in sorted(files):
194
+ filepath = os.path.join(functions_dir, f"{name}.py")
195
+ # Read first line of docstring
196
+ with open(filepath) as f:
197
+ content = f.read()
198
+ desc = ""
199
+ if '"""' in content:
200
+ start = content.index('"""') + 3
201
+ end = content.index('"""', start)
202
+ desc = content[start:end].strip().split("\n")[0]
203
+ print(f" {name:20s} {desc}")
204
+
205
+
206
+ def _cmd_create(description, name, as_skill, provider=None, model=None):
207
+ """Create a new function."""
208
+ from agentic.meta_functions import create
209
+ runtime = _get_runtime(provider, model)
210
+
211
+ print(f"Creating '{name}' (provider: {runtime.__class__.__name__})...")
212
+ fn = create(description=description, runtime=runtime, name=name, as_skill=as_skill)
213
+ print(f" Saved to agentic/functions/{name}.py")
214
+ if as_skill:
215
+ print(f" Skill created at skills/{name}/SKILL.md")
216
+
217
+
218
+ def _cmd_create_app(description, name, provider=None, model=None):
219
+ """Create a complete runnable app."""
220
+ from agentic.meta_functions import create_app
221
+ runtime = _get_runtime(provider, model)
222
+
223
+ print(f"Creating app '{name}' (provider: {runtime.__class__.__name__})...")
224
+ filepath = create_app(description=description, runtime=runtime, name=name)
225
+ print(f" Saved to {filepath}")
226
+ print(f" Run with: python {filepath}")
227
+
228
+
229
+ def _cmd_fix(name, instruction, provider=None, model=None):
230
+ """Fix an existing function."""
231
+ import importlib
232
+ from agentic.meta_functions import fix
233
+ runtime = _get_runtime(provider, model)
234
+
235
+ try:
236
+ mod = importlib.import_module(f"agentic.functions.{name}")
237
+ fn = getattr(mod, name)
238
+ except (ImportError, AttributeError):
239
+ print(f"Error: function '{name}' not found in agentic/functions/")
240
+ sys.exit(1)
241
+
242
+ print(f"Fixing '{name}' (provider: {runtime.__class__.__name__})...")
243
+ fixed = fix(fn=fn, runtime=runtime, instruction=instruction)
244
+ print(f" Fixed and saved to agentic/functions/{name}.py")
245
+
246
+
247
+ def _cmd_run(name, arg_list, provider=None, model=None):
248
+ """Run an existing function."""
249
+ import importlib
250
+
251
+ try:
252
+ mod = importlib.import_module(f"agentic.functions.{name}")
253
+ fn = getattr(mod, name)
254
+ except (ImportError, AttributeError):
255
+ print(f"Error: function '{name}' not found in agentic/functions/")
256
+ sys.exit(1)
257
+
258
+ # Check if it needs runtime
259
+ import inspect
260
+ source = inspect.getsource(fn) if hasattr(fn, '_fn') else ""
261
+ if hasattr(fn, '_fn'):
262
+ try:
263
+ source = inspect.getsource(fn._fn)
264
+ except (OSError, TypeError):
265
+ source = ""
266
+
267
+ if "runtime.exec" in source or "runtime" in str(getattr(fn, '__globals__', {})):
268
+ runtime = _get_runtime(provider, model)
269
+ if hasattr(fn, '_fn') and fn._fn:
270
+ fn._fn.__globals__['runtime'] = runtime
271
+ elif hasattr(fn, '__globals__'):
272
+ fn.__globals__['runtime'] = runtime
273
+
274
+ # Parse arguments
275
+ kwargs = {}
276
+ for a in arg_list:
277
+ if "=" in a:
278
+ k, v = a.split("=", 1)
279
+ kwargs[k] = v
280
+ else:
281
+ print(f"Error: argument must be key=value, got '{a}'")
282
+ sys.exit(1)
283
+
284
+ result = fn(**kwargs)
285
+ print(result)
286
+
287
+
288
+ def _cmd_create_skill(name, provider=None, model=None):
289
+ """Create a SKILL.md for a function."""
290
+ import importlib
291
+ import inspect
292
+ from agentic.meta_functions import create_skill
293
+ runtime = _get_runtime(provider, model)
294
+
295
+ try:
296
+ mod = importlib.import_module(f"agentic.functions.{name}")
297
+ fn = getattr(mod, name)
298
+ except (ImportError, AttributeError):
299
+ print(f"Error: function '{name}' not found in agentic/functions/")
300
+ sys.exit(1)
301
+
302
+ # Get source and description
303
+ try:
304
+ if hasattr(fn, '_fn'):
305
+ code = inspect.getsource(fn._fn)
306
+ else:
307
+ code = inspect.getsource(fn)
308
+ except (OSError, TypeError):
309
+ code = f"# Source not available for {name}"
310
+
311
+ description = getattr(fn, '__doc__', '') or name
312
+
313
+ print(f"Creating skill for '{name}'...")
314
+ path = create_skill(fn_name=name, description=description, code=code, runtime=runtime)
315
+ print(f" Skill created at {path}")
316
+
317
+
318
+ if __name__ == "__main__":
319
+ main()