janito 3.12.0__py3-none-any.whl → 3.12.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.
- janito/README.md +191 -191
- janito/_version.py +55 -55
- janito/agent/setup_agent.py +378 -377
- janito/cli/chat_mode/session.py +505 -505
- janito/cli/cli_commands/list_drivers.py +162 -162
- janito/cli/cli_commands/list_profiles.py +104 -107
- janito/cli/cli_commands/show_system_prompt.py +166 -166
- janito/cli/console.py +3 -3
- janito/cli/core/runner.py +250 -266
- janito/cli/main_cli.py +520 -519
- janito/cli/rich_terminal_reporter.py +2 -4
- janito/cli/single_shot_mode/handler.py +167 -167
- janito/docs/GETTING_STARTED.md +189 -189
- janito/llm/__init__.py +6 -5
- janito/llm/driver.py +290 -254
- janito/llm/response_cache.py +57 -0
- janito/plugins/builtin.py +64 -88
- janito/plugins/discovery.py +289 -289
- janito/plugins/tools/local/__init__.py +82 -80
- janito/plugins/tools/local/markdown_view.py +94 -0
- janito/plugins/tools/local/show_image.py +119 -74
- janito/plugins/tools/local/show_image_grid.py +134 -76
- janito/providers/alibaba/model_info.py +136 -105
- {janito-3.12.0.dist-info → janito-3.12.2.dist-info}/METADATA +5 -4
- {janito-3.12.0.dist-info → janito-3.12.2.dist-info}/RECORD +29 -28
- janito/mkdocs.yml +0 -40
- {janito-3.12.0.dist-info → janito-3.12.2.dist-info}/WHEEL +0 -0
- {janito-3.12.0.dist-info → janito-3.12.2.dist-info}/entry_points.txt +0 -0
- {janito-3.12.0.dist-info → janito-3.12.2.dist-info}/licenses/LICENSE +0 -0
- {janito-3.12.0.dist-info → janito-3.12.2.dist-info}/top_level.txt +0 -0
@@ -1,162 +1,162 @@
|
|
1
|
-
"""
|
2
|
-
CLI Command: List available LLM drivers and their dependencies
|
3
|
-
"""
|
4
|
-
|
5
|
-
import importlib
|
6
|
-
import sys
|
7
|
-
from pathlib import Path
|
8
|
-
from rich.console import Console
|
9
|
-
from rich.table import Table
|
10
|
-
from rich.panel import Panel
|
11
|
-
from rich.text import Text
|
12
|
-
|
13
|
-
console = Console()
|
14
|
-
|
15
|
-
|
16
|
-
def _detect_dependencies_from_content(content, class_name):
|
17
|
-
"""Detect dependencies from module content."""
|
18
|
-
dependencies = []
|
19
|
-
|
20
|
-
if "import openai" in content or "from openai" in content:
|
21
|
-
dependencies.append("openai")
|
22
|
-
if "import zai" in content or "from zai" in content:
|
23
|
-
dependencies.append("zai")
|
24
|
-
if "import anthropic" in content or "from anthropic" in content:
|
25
|
-
dependencies.append("anthropic")
|
26
|
-
if "import google" in content or "from google" in content:
|
27
|
-
dependencies.append("google-generativeai")
|
28
|
-
|
29
|
-
# Remove openai from zai driver dependencies
|
30
|
-
if "ZAIModelDriver" in class_name and "openai" in dependencies:
|
31
|
-
dependencies.remove("openai")
|
32
|
-
|
33
|
-
return dependencies
|
34
|
-
|
35
|
-
|
36
|
-
def _check_dependency_status(dependencies):
|
37
|
-
"""Check if dependencies are available."""
|
38
|
-
if not dependencies:
|
39
|
-
return ["No external dependencies"]
|
40
|
-
|
41
|
-
dep_status = []
|
42
|
-
for dep in dependencies:
|
43
|
-
try:
|
44
|
-
importlib.import_module(dep)
|
45
|
-
dep_status.append(f"✅ {dep}")
|
46
|
-
except ImportError:
|
47
|
-
dep_status.append(f"❌ {dep}")
|
48
|
-
|
49
|
-
return dep_status
|
50
|
-
|
51
|
-
|
52
|
-
def _get_single_driver_info(module_path, class_name):
|
53
|
-
"""Get information for a single driver."""
|
54
|
-
try:
|
55
|
-
module = importlib.import_module(module_path)
|
56
|
-
driver_class = getattr(module, class_name)
|
57
|
-
|
58
|
-
available = getattr(driver_class, "available", True)
|
59
|
-
unavailable_reason = getattr(driver_class, "unavailable_reason", None)
|
60
|
-
|
61
|
-
# Read module file to detect imports
|
62
|
-
module_file = Path(module.__file__)
|
63
|
-
with open(module_file, "r", encoding="utf-8") as f:
|
64
|
-
content = f.read()
|
65
|
-
|
66
|
-
dependencies = _detect_dependencies_from_content(content, class_name)
|
67
|
-
dep_status = _check_dependency_status(dependencies)
|
68
|
-
|
69
|
-
return {
|
70
|
-
"name": class_name,
|
71
|
-
"available": available,
|
72
|
-
"reason": unavailable_reason,
|
73
|
-
"dependencies": dep_status,
|
74
|
-
}
|
75
|
-
|
76
|
-
except (ImportError, AttributeError) as e:
|
77
|
-
return {
|
78
|
-
"name": class_name,
|
79
|
-
"module": module_path,
|
80
|
-
"available": False,
|
81
|
-
"reason": str(e),
|
82
|
-
"dependencies": ["❌ Module not found"],
|
83
|
-
}
|
84
|
-
|
85
|
-
|
86
|
-
def get_driver_info():
|
87
|
-
"""Get information about all available drivers."""
|
88
|
-
drivers = []
|
89
|
-
|
90
|
-
# Define known driver modules
|
91
|
-
driver_modules = [
|
92
|
-
("janito.drivers.openai.driver", "OpenAIModelDriver"),
|
93
|
-
("janito.drivers.azure_openai.driver", "AzureOpenAIModelDriver"),
|
94
|
-
("janito.drivers.zai.driver", "ZAIModelDriver"),
|
95
|
-
("janito.drivers.cerebras.driver", "CerebrasModelDriver"),
|
96
|
-
]
|
97
|
-
|
98
|
-
for module_path, class_name in driver_modules:
|
99
|
-
driver_info = _get_single_driver_info(module_path, class_name)
|
100
|
-
drivers.append(driver_info)
|
101
|
-
|
102
|
-
return drivers
|
103
|
-
|
104
|
-
|
105
|
-
def _create_driver_table(drivers):
|
106
|
-
"""Create and populate the drivers table."""
|
107
|
-
table = Table(title="Available LLM Drivers")
|
108
|
-
table.add_column("Driver", style="cyan", no_wrap=True)
|
109
|
-
table.add_column("Status", style="bold")
|
110
|
-
table.add_column("Dependencies", style="yellow")
|
111
|
-
|
112
|
-
for driver in drivers:
|
113
|
-
name = driver["name"]
|
114
|
-
|
115
|
-
if driver["available"]:
|
116
|
-
status = "[green]✅ Available[/green]"
|
117
|
-
if driver["reason"]:
|
118
|
-
status = f"[yellow]⚠️ Available ({driver['reason']})[/yellow]"
|
119
|
-
else:
|
120
|
-
status = f"[red]❌ Unavailable[/red]"
|
121
|
-
if driver["reason"]:
|
122
|
-
status = f"[red]❌ {driver['reason']}[/red]"
|
123
|
-
|
124
|
-
deps = "\n".join(driver["dependencies"])
|
125
|
-
table.add_row(name, status, deps)
|
126
|
-
|
127
|
-
return table
|
128
|
-
|
129
|
-
|
130
|
-
def _get_missing_dependencies(drivers):
|
131
|
-
"""Get list of missing dependencies."""
|
132
|
-
missing_deps = []
|
133
|
-
for driver in drivers:
|
134
|
-
for dep_status in driver["dependencies"]:
|
135
|
-
if dep_status.startswith("❌"):
|
136
|
-
dep_name = dep_status.split()[1]
|
137
|
-
if dep_name not in missing_deps:
|
138
|
-
missing_deps.append(dep_name)
|
139
|
-
return missing_deps
|
140
|
-
|
141
|
-
|
142
|
-
def handle_list_drivers(args=None):
|
143
|
-
"""List all available LLM drivers with their status and dependencies."""
|
144
|
-
drivers = get_driver_info()
|
145
|
-
|
146
|
-
if not drivers:
|
147
|
-
console.print("[red]No drivers found[/red]")
|
148
|
-
return
|
149
|
-
|
150
|
-
table = _create_driver_table(drivers)
|
151
|
-
console.print(table)
|
152
|
-
|
153
|
-
# Installation help - only show for missing dependencies
|
154
|
-
missing_deps = _get_missing_dependencies(drivers)
|
155
|
-
if missing_deps:
|
156
|
-
console.print(
|
157
|
-
f"\n[dim]💡 Install missing deps: pip install {' '.join(missing_deps)}[/dim]"
|
158
|
-
)
|
159
|
-
|
160
|
-
|
161
|
-
if __name__ == "__main__":
|
162
|
-
handle_list_drivers()
|
1
|
+
"""
|
2
|
+
CLI Command: List available LLM drivers and their dependencies
|
3
|
+
"""
|
4
|
+
|
5
|
+
import importlib
|
6
|
+
import sys
|
7
|
+
from pathlib import Path
|
8
|
+
from rich.console import Console
|
9
|
+
from rich.table import Table
|
10
|
+
from rich.panel import Panel
|
11
|
+
from rich.text import Text
|
12
|
+
|
13
|
+
console = Console()
|
14
|
+
|
15
|
+
|
16
|
+
def _detect_dependencies_from_content(content, class_name):
|
17
|
+
"""Detect dependencies from module content."""
|
18
|
+
dependencies = []
|
19
|
+
|
20
|
+
if "import openai" in content or "from openai" in content:
|
21
|
+
dependencies.append("openai")
|
22
|
+
if "import zai" in content or "from zai" in content:
|
23
|
+
dependencies.append("zai")
|
24
|
+
if "import anthropic" in content or "from anthropic" in content:
|
25
|
+
dependencies.append("anthropic")
|
26
|
+
if "import google" in content or "from google" in content:
|
27
|
+
dependencies.append("google-generativeai")
|
28
|
+
|
29
|
+
# Remove openai from zai driver dependencies
|
30
|
+
if "ZAIModelDriver" in class_name and "openai" in dependencies:
|
31
|
+
dependencies.remove("openai")
|
32
|
+
|
33
|
+
return dependencies
|
34
|
+
|
35
|
+
|
36
|
+
def _check_dependency_status(dependencies):
|
37
|
+
"""Check if dependencies are available."""
|
38
|
+
if not dependencies:
|
39
|
+
return ["No external dependencies"]
|
40
|
+
|
41
|
+
dep_status = []
|
42
|
+
for dep in dependencies:
|
43
|
+
try:
|
44
|
+
importlib.import_module(dep)
|
45
|
+
dep_status.append(f"✅ {dep}")
|
46
|
+
except ImportError:
|
47
|
+
dep_status.append(f"❌ {dep}")
|
48
|
+
|
49
|
+
return dep_status
|
50
|
+
|
51
|
+
|
52
|
+
def _get_single_driver_info(module_path, class_name):
|
53
|
+
"""Get information for a single driver."""
|
54
|
+
try:
|
55
|
+
module = importlib.import_module(module_path)
|
56
|
+
driver_class = getattr(module, class_name)
|
57
|
+
|
58
|
+
available = getattr(driver_class, "available", True)
|
59
|
+
unavailable_reason = getattr(driver_class, "unavailable_reason", None)
|
60
|
+
|
61
|
+
# Read module file to detect imports
|
62
|
+
module_file = Path(module.__file__)
|
63
|
+
with open(module_file, "r", encoding="utf-8") as f:
|
64
|
+
content = f.read()
|
65
|
+
|
66
|
+
dependencies = _detect_dependencies_from_content(content, class_name)
|
67
|
+
dep_status = _check_dependency_status(dependencies)
|
68
|
+
|
69
|
+
return {
|
70
|
+
"name": class_name,
|
71
|
+
"available": available,
|
72
|
+
"reason": unavailable_reason,
|
73
|
+
"dependencies": dep_status,
|
74
|
+
}
|
75
|
+
|
76
|
+
except (ImportError, AttributeError) as e:
|
77
|
+
return {
|
78
|
+
"name": class_name,
|
79
|
+
"module": module_path,
|
80
|
+
"available": False,
|
81
|
+
"reason": str(e),
|
82
|
+
"dependencies": ["❌ Module not found"],
|
83
|
+
}
|
84
|
+
|
85
|
+
|
86
|
+
def get_driver_info():
|
87
|
+
"""Get information about all available drivers."""
|
88
|
+
drivers = []
|
89
|
+
|
90
|
+
# Define known driver modules
|
91
|
+
driver_modules = [
|
92
|
+
("janito.drivers.openai.driver", "OpenAIModelDriver"),
|
93
|
+
("janito.drivers.azure_openai.driver", "AzureOpenAIModelDriver"),
|
94
|
+
("janito.drivers.zai.driver", "ZAIModelDriver"),
|
95
|
+
("janito.drivers.cerebras.driver", "CerebrasModelDriver"),
|
96
|
+
]
|
97
|
+
|
98
|
+
for module_path, class_name in driver_modules:
|
99
|
+
driver_info = _get_single_driver_info(module_path, class_name)
|
100
|
+
drivers.append(driver_info)
|
101
|
+
|
102
|
+
return drivers
|
103
|
+
|
104
|
+
|
105
|
+
def _create_driver_table(drivers):
|
106
|
+
"""Create and populate the drivers table."""
|
107
|
+
table = Table(title="Available LLM Drivers")
|
108
|
+
table.add_column("Driver", style="cyan", no_wrap=True)
|
109
|
+
table.add_column("Status", style="bold")
|
110
|
+
table.add_column("Dependencies", style="yellow")
|
111
|
+
|
112
|
+
for driver in drivers:
|
113
|
+
name = driver["name"]
|
114
|
+
|
115
|
+
if driver["available"]:
|
116
|
+
status = "[green]✅ Available[/green]"
|
117
|
+
if driver["reason"]:
|
118
|
+
status = f"[yellow]⚠️ Available ({driver['reason']})[/yellow]"
|
119
|
+
else:
|
120
|
+
status = f"[red]❌ Unavailable[/red]"
|
121
|
+
if driver["reason"]:
|
122
|
+
status = f"[red]❌ {driver['reason']}[/red]"
|
123
|
+
|
124
|
+
deps = "\n".join(driver["dependencies"])
|
125
|
+
table.add_row(name, status, deps)
|
126
|
+
|
127
|
+
return table
|
128
|
+
|
129
|
+
|
130
|
+
def _get_missing_dependencies(drivers):
|
131
|
+
"""Get list of missing dependencies."""
|
132
|
+
missing_deps = []
|
133
|
+
for driver in drivers:
|
134
|
+
for dep_status in driver["dependencies"]:
|
135
|
+
if dep_status.startswith("❌"):
|
136
|
+
dep_name = dep_status.split()[1]
|
137
|
+
if dep_name not in missing_deps:
|
138
|
+
missing_deps.append(dep_name)
|
139
|
+
return missing_deps
|
140
|
+
|
141
|
+
|
142
|
+
def handle_list_drivers(args=None):
|
143
|
+
"""List all available LLM drivers with their status and dependencies."""
|
144
|
+
drivers = get_driver_info()
|
145
|
+
|
146
|
+
if not drivers:
|
147
|
+
console.print("[red]No drivers found[/red]")
|
148
|
+
return
|
149
|
+
|
150
|
+
table = _create_driver_table(drivers)
|
151
|
+
console.print(table)
|
152
|
+
|
153
|
+
# Installation help - only show for missing dependencies
|
154
|
+
missing_deps = _get_missing_dependencies(drivers)
|
155
|
+
if missing_deps:
|
156
|
+
console.print(
|
157
|
+
f"\n[dim]💡 Install missing deps: uv pip install {' '.join(missing_deps)}[/dim]"
|
158
|
+
)
|
159
|
+
|
160
|
+
|
161
|
+
if __name__ == "__main__":
|
162
|
+
handle_list_drivers()
|
@@ -1,107 +1,104 @@
|
|
1
|
-
"""
|
2
|
-
CLI Command: List available system prompt profiles (default and user-specific)
|
3
|
-
"""
|
4
|
-
|
5
|
-
from pathlib import Path
|
6
|
-
import importlib.resources as resources
|
7
|
-
from rich.console import Console
|
8
|
-
from rich.table import Table
|
9
|
-
|
10
|
-
|
11
|
-
_PREFIX = "system_prompt_template_"
|
12
|
-
_SUFFIX = ".txt.j2"
|
13
|
-
|
14
|
-
|
15
|
-
def _extract_profile_name(filename: str) -> str:
|
16
|
-
"""Return the human-readable profile name from template file name."""
|
17
|
-
# Remove prefix & suffix and convert underscores back to spaces
|
18
|
-
if filename.startswith(_PREFIX):
|
19
|
-
filename = filename[len(_PREFIX) :]
|
20
|
-
if filename.endswith(_SUFFIX):
|
21
|
-
filename = filename[: -len(_SUFFIX)]
|
22
|
-
|
23
|
-
# Convert to title case for consistent capitalization, but handle common acronyms
|
24
|
-
name = filename.replace("_", " ")
|
25
|
-
|
26
|
-
#
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
"
|
32
|
-
"
|
33
|
-
"
|
34
|
-
"
|
35
|
-
"
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
default_profiles
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
_print_profiles_table(default_profiles, user_profiles)
|
107
|
-
return
|
1
|
+
"""
|
2
|
+
CLI Command: List available system prompt profiles (default and user-specific)
|
3
|
+
"""
|
4
|
+
|
5
|
+
from pathlib import Path
|
6
|
+
import importlib.resources as resources
|
7
|
+
from rich.console import Console
|
8
|
+
from rich.table import Table
|
9
|
+
|
10
|
+
|
11
|
+
_PREFIX = "system_prompt_template_"
|
12
|
+
_SUFFIX = ".txt.j2"
|
13
|
+
|
14
|
+
|
15
|
+
def _extract_profile_name(filename: str) -> str:
|
16
|
+
"""Return the human-readable profile name from template file name."""
|
17
|
+
# Remove prefix & suffix and convert underscores back to spaces
|
18
|
+
if filename.startswith(_PREFIX):
|
19
|
+
filename = filename[len(_PREFIX) :]
|
20
|
+
if filename.endswith(_SUFFIX):
|
21
|
+
filename = filename[: -len(_SUFFIX)]
|
22
|
+
|
23
|
+
# Convert to title case for consistent capitalization, but handle common acronyms
|
24
|
+
name = filename.replace("_", " ")
|
25
|
+
|
26
|
+
# Handle special cases and acronyms
|
27
|
+
special_cases = {
|
28
|
+
"python": "Python",
|
29
|
+
"tools": "Tools",
|
30
|
+
"model": "Model",
|
31
|
+
"context": "Context",
|
32
|
+
"developer": "Developer",
|
33
|
+
"analyst": "Analyst",
|
34
|
+
"conversation": "Conversation",
|
35
|
+
"without": "Without",
|
36
|
+
}
|
37
|
+
|
38
|
+
words = name.split()
|
39
|
+
capitalized_words = []
|
40
|
+
for word in words:
|
41
|
+
lower_word = word.lower()
|
42
|
+
if lower_word in special_cases:
|
43
|
+
capitalized_words.append(special_cases[lower_word])
|
44
|
+
else:
|
45
|
+
capitalized_words.append(word.capitalize())
|
46
|
+
|
47
|
+
return " ".join(capitalized_words)
|
48
|
+
|
49
|
+
|
50
|
+
def _gather_default_profiles():
|
51
|
+
"""Return list of built-in profile names bundled with janito."""
|
52
|
+
profiles = []
|
53
|
+
try:
|
54
|
+
package_files = resources.files("janito.agent.templates.profiles")
|
55
|
+
for path in package_files.iterdir():
|
56
|
+
name = path.name
|
57
|
+
if name.startswith(_PREFIX) and name.endswith(_SUFFIX):
|
58
|
+
profiles.append(_extract_profile_name(name))
|
59
|
+
except Exception:
|
60
|
+
# If for some reason the resources are not available fall back to empty list
|
61
|
+
pass
|
62
|
+
return sorted(profiles, key=str.lower)
|
63
|
+
|
64
|
+
|
65
|
+
def _gather_user_profiles():
|
66
|
+
"""Return list of user-defined profile names from ~/.janito/profiles directory."""
|
67
|
+
user_dir = Path.home() / ".janito" / "profiles"
|
68
|
+
profiles = []
|
69
|
+
if user_dir.exists() and user_dir.is_dir():
|
70
|
+
for path in user_dir.iterdir():
|
71
|
+
if (
|
72
|
+
path.is_file()
|
73
|
+
and path.name.startswith(_PREFIX)
|
74
|
+
and path.name.endswith(_SUFFIX)
|
75
|
+
):
|
76
|
+
profiles.append(_extract_profile_name(path.name))
|
77
|
+
return sorted(profiles, key=str.lower)
|
78
|
+
|
79
|
+
|
80
|
+
def _print_profiles_table(default_profiles, user_profiles):
|
81
|
+
console = Console()
|
82
|
+
table = Table(title="Available System Prompt Profiles", box=None, show_lines=False)
|
83
|
+
table.add_column("Profile Name", style="cyan", no_wrap=False)
|
84
|
+
table.add_column("Source", style="magenta", no_wrap=True)
|
85
|
+
|
86
|
+
for p in default_profiles:
|
87
|
+
table.add_row(p, "default")
|
88
|
+
for p in user_profiles:
|
89
|
+
table.add_row(p, "user")
|
90
|
+
|
91
|
+
console.print(table)
|
92
|
+
|
93
|
+
|
94
|
+
def handle_list_profiles(args=None):
|
95
|
+
"""Entry point for the --list-profiles CLI flag."""
|
96
|
+
default_profiles = _gather_default_profiles()
|
97
|
+
user_profiles = _gather_user_profiles()
|
98
|
+
|
99
|
+
if not default_profiles and not user_profiles:
|
100
|
+
print("No profiles found.")
|
101
|
+
return
|
102
|
+
|
103
|
+
_print_profiles_table(default_profiles, user_profiles)
|
104
|
+
return
|