h2ogpte 1.6.42__py3-none-any.whl → 1.6.43__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.
- h2ogpte/__init__.py +1 -1
- h2ogpte/cli/__init__.py +0 -0
- h2ogpte/cli/commands/__init__.py +0 -0
- h2ogpte/cli/commands/command_handlers/__init__.py +0 -0
- h2ogpte/cli/commands/command_handlers/agent.py +41 -0
- h2ogpte/cli/commands/command_handlers/chat.py +37 -0
- h2ogpte/cli/commands/command_handlers/clear.py +8 -0
- h2ogpte/cli/commands/command_handlers/collection.py +67 -0
- h2ogpte/cli/commands/command_handlers/config.py +113 -0
- h2ogpte/cli/commands/command_handlers/disconnect.py +36 -0
- h2ogpte/cli/commands/command_handlers/exit.py +37 -0
- h2ogpte/cli/commands/command_handlers/help.py +8 -0
- h2ogpte/cli/commands/command_handlers/history.py +29 -0
- h2ogpte/cli/commands/command_handlers/rag.py +146 -0
- h2ogpte/cli/commands/command_handlers/research_agent.py +45 -0
- h2ogpte/cli/commands/command_handlers/session.py +77 -0
- h2ogpte/cli/commands/command_handlers/status.py +33 -0
- h2ogpte/cli/commands/dispatcher.py +79 -0
- h2ogpte/cli/core/__init__.py +0 -0
- h2ogpte/cli/core/app.py +105 -0
- h2ogpte/cli/core/config.py +199 -0
- h2ogpte/cli/core/encryption.py +104 -0
- h2ogpte/cli/core/session.py +171 -0
- h2ogpte/cli/integrations/__init__.py +0 -0
- h2ogpte/cli/integrations/agent.py +338 -0
- h2ogpte/cli/integrations/rag.py +442 -0
- h2ogpte/cli/main.py +90 -0
- h2ogpte/cli/ui/__init__.py +0 -0
- h2ogpte/cli/ui/hbot_prompt.py +435 -0
- h2ogpte/cli/ui/prompts.py +129 -0
- h2ogpte/cli/ui/status_bar.py +133 -0
- h2ogpte/cli/utils/__init__.py +0 -0
- h2ogpte/cli/utils/file_manager.py +411 -0
- h2ogpte/connectors.py +11 -0
- h2ogpte/h2ogpte.py +619 -69
- h2ogpte/h2ogpte_async.py +631 -70
- h2ogpte/h2ogpte_sync_base.py +8 -1
- h2ogpte/rest_async/__init__.py +8 -3
- h2ogpte/rest_async/api/chat_api.py +29 -0
- h2ogpte/rest_async/api/collections_api.py +293 -0
- h2ogpte/rest_async/api/document_ingestion_api.py +1365 -436
- h2ogpte/rest_async/api/extractors_api.py +2874 -70
- h2ogpte/rest_async/api/prompt_templates_api.py +32 -32
- h2ogpte/rest_async/api_client.py +1 -1
- h2ogpte/rest_async/configuration.py +1 -1
- h2ogpte/rest_async/models/__init__.py +7 -2
- h2ogpte/rest_async/models/chat_completion.py +4 -2
- h2ogpte/rest_async/models/chat_completion_delta.py +5 -3
- h2ogpte/rest_async/models/chat_completion_request.py +1 -1
- h2ogpte/rest_async/models/chat_session.py +4 -2
- h2ogpte/rest_async/models/chat_settings.py +1 -1
- h2ogpte/rest_async/models/collection.py +4 -2
- h2ogpte/rest_async/models/collection_create_request.py +4 -2
- h2ogpte/rest_async/models/confluence_credentials.py +89 -0
- h2ogpte/rest_async/models/create_chat_session_request.py +87 -0
- h2ogpte/rest_async/models/extraction_request.py +1 -1
- h2ogpte/rest_async/models/extractor.py +4 -2
- h2ogpte/rest_async/models/guardrails_settings.py +8 -4
- h2ogpte/rest_async/models/guardrails_settings_create_request.py +1 -1
- h2ogpte/rest_async/models/ingest_from_confluence_body.py +97 -0
- h2ogpte/rest_async/models/process_document_job_request.py +1 -1
- h2ogpte/rest_async/models/question_request.py +1 -1
- h2ogpte/rest_async/models/{reset_and_share_prompt_template_request.py → reset_and_share_request.py} +6 -6
- h2ogpte/{rest_sync/models/reset_and_share_prompt_template_with_groups_request.py → rest_async/models/reset_and_share_with_groups_request.py} +6 -6
- h2ogpte/rest_async/models/summarize_request.py +1 -1
- h2ogpte/rest_async/models/update_collection_privacy_request.py +6 -4
- h2ogpte/rest_async/models/update_collection_workspace_request.py +87 -0
- h2ogpte/rest_async/models/update_extractor_privacy_request.py +87 -0
- h2ogpte/rest_sync/__init__.py +8 -3
- h2ogpte/rest_sync/api/chat_api.py +29 -0
- h2ogpte/rest_sync/api/collections_api.py +293 -0
- h2ogpte/rest_sync/api/document_ingestion_api.py +1365 -436
- h2ogpte/rest_sync/api/extractors_api.py +2874 -70
- h2ogpte/rest_sync/api/prompt_templates_api.py +32 -32
- h2ogpte/rest_sync/api_client.py +1 -1
- h2ogpte/rest_sync/configuration.py +1 -1
- h2ogpte/rest_sync/models/__init__.py +7 -2
- h2ogpte/rest_sync/models/chat_completion.py +4 -2
- h2ogpte/rest_sync/models/chat_completion_delta.py +5 -3
- h2ogpte/rest_sync/models/chat_completion_request.py +1 -1
- h2ogpte/rest_sync/models/chat_session.py +4 -2
- h2ogpte/rest_sync/models/chat_settings.py +1 -1
- h2ogpte/rest_sync/models/collection.py +4 -2
- h2ogpte/rest_sync/models/collection_create_request.py +4 -2
- h2ogpte/rest_sync/models/confluence_credentials.py +89 -0
- h2ogpte/rest_sync/models/create_chat_session_request.py +87 -0
- h2ogpte/rest_sync/models/extraction_request.py +1 -1
- h2ogpte/rest_sync/models/extractor.py +4 -2
- h2ogpte/rest_sync/models/guardrails_settings.py +8 -4
- h2ogpte/rest_sync/models/guardrails_settings_create_request.py +1 -1
- h2ogpte/rest_sync/models/ingest_from_confluence_body.py +97 -0
- h2ogpte/rest_sync/models/process_document_job_request.py +1 -1
- h2ogpte/rest_sync/models/question_request.py +1 -1
- h2ogpte/rest_sync/models/{reset_and_share_prompt_template_request.py → reset_and_share_request.py} +6 -6
- h2ogpte/{rest_async/models/reset_and_share_prompt_template_with_groups_request.py → rest_sync/models/reset_and_share_with_groups_request.py} +6 -6
- h2ogpte/rest_sync/models/summarize_request.py +1 -1
- h2ogpte/rest_sync/models/update_collection_privacy_request.py +6 -4
- h2ogpte/rest_sync/models/update_collection_workspace_request.py +87 -0
- h2ogpte/rest_sync/models/update_extractor_privacy_request.py +87 -0
- h2ogpte/session.py +14 -2
- h2ogpte/session_async.py +33 -6
- h2ogpte/types.py +9 -1
- {h2ogpte-1.6.42.dist-info → h2ogpte-1.6.43.dist-info}/METADATA +5 -1
- {h2ogpte-1.6.42.dist-info → h2ogpte-1.6.43.dist-info}/RECORD +107 -64
- h2ogpte-1.6.43.dist-info/entry_points.txt +2 -0
- {h2ogpte-1.6.42.dist-info → h2ogpte-1.6.43.dist-info}/WHEEL +0 -0
- {h2ogpte-1.6.42.dist-info → h2ogpte-1.6.43.dist-info}/top_level.txt +0 -0
h2ogpte/__init__.py
CHANGED
h2ogpte/cli/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from ...core.app import get_app_state
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
async def handle_agent(args: str) -> bool:
|
|
5
|
+
app = get_app_state()
|
|
6
|
+
|
|
7
|
+
if not args or not args.strip():
|
|
8
|
+
app.ui.show_error("Please provide a message")
|
|
9
|
+
app.ui.show_info("Example: /agent Help me analyze this code")
|
|
10
|
+
return True
|
|
11
|
+
|
|
12
|
+
message = args.strip()
|
|
13
|
+
|
|
14
|
+
if app.rag_manager.connected:
|
|
15
|
+
usage_stats = await app.rag_manager.send_message(message, use_agent=True)
|
|
16
|
+
|
|
17
|
+
if usage_stats:
|
|
18
|
+
total_input_tokens = sum(
|
|
19
|
+
u.get("input_tokens", 0) for u in usage_stats.get("usage", [])
|
|
20
|
+
)
|
|
21
|
+
total_output_tokens = sum(
|
|
22
|
+
u.get("output_tokens", 0) for u in usage_stats.get("usage", [])
|
|
23
|
+
)
|
|
24
|
+
response_time = usage_stats.get("response_time", "N/A")
|
|
25
|
+
queued_for = usage_stats.get("queue_time", "N/A")
|
|
26
|
+
llm = usage_stats.get("llm", "N/A")
|
|
27
|
+
app.ui.show_info(
|
|
28
|
+
f"[Agent] Tokens: {total_input_tokens} (in), {total_output_tokens} (out), "
|
|
29
|
+
f"Response time: {response_time}, "
|
|
30
|
+
f"Queued for: {queued_for}, "
|
|
31
|
+
f"LLM: {llm}"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
await app.update_status_bar()
|
|
35
|
+
else:
|
|
36
|
+
app.ui.show_error("Not connected to H2OGPTE. Use /register to connect first.")
|
|
37
|
+
app.ui.show_info(
|
|
38
|
+
"Example: /register https://your-h2ogpte-endpoint.com your-api-key"
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
return True
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from ...core.app import get_app_state
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
async def handle_chat(message: str) -> bool:
|
|
5
|
+
app = get_app_state()
|
|
6
|
+
|
|
7
|
+
if not message or not message.strip():
|
|
8
|
+
return True
|
|
9
|
+
|
|
10
|
+
if app.rag_manager.connected:
|
|
11
|
+
usage_stats = await app.rag_manager.send_message(message)
|
|
12
|
+
|
|
13
|
+
if usage_stats:
|
|
14
|
+
total_input_tokens = sum(
|
|
15
|
+
u.get("input_tokens", 0) for u in usage_stats.get("usage", [])
|
|
16
|
+
)
|
|
17
|
+
total_output_tokens = sum(
|
|
18
|
+
u.get("output_tokens", 0) for u in usage_stats.get("usage", [])
|
|
19
|
+
)
|
|
20
|
+
response_time = usage_stats.get("response_time", "N/A")
|
|
21
|
+
queued_for = usage_stats.get("queue_time", "N/A")
|
|
22
|
+
llm = usage_stats.get("llm", "N/A")
|
|
23
|
+
app.ui.show_info(
|
|
24
|
+
f"Tokens: {total_input_tokens} (in), {total_output_tokens} (out), "
|
|
25
|
+
f"Response time: {response_time}, "
|
|
26
|
+
f"Queued for: {queued_for}, "
|
|
27
|
+
f"LLM: {llm}"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
await app.update_status_bar()
|
|
31
|
+
else:
|
|
32
|
+
app.ui.show_error("Not connected to H2OGPTE. Use /register to connect first.")
|
|
33
|
+
app.ui.show_info(
|
|
34
|
+
"Example: /register https://your-h2ogpte-endpoint.com your-api-key"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
return True
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from ...core.app import get_app_state
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
async def handle_create_collection(args: str) -> bool:
|
|
5
|
+
"""Create a new collection and switch to it."""
|
|
6
|
+
app = get_app_state()
|
|
7
|
+
|
|
8
|
+
if not app.rag_manager.connected:
|
|
9
|
+
app.ui.show_error("Not connected to H2OGPTE. Use /register first.")
|
|
10
|
+
return True
|
|
11
|
+
|
|
12
|
+
if not args:
|
|
13
|
+
collection_name = app.ui.prompt.get_input_with_default(
|
|
14
|
+
"Enter collection name", "CLI-Collection"
|
|
15
|
+
)
|
|
16
|
+
else:
|
|
17
|
+
collection_name = args.strip()
|
|
18
|
+
|
|
19
|
+
if collection_name:
|
|
20
|
+
app.ui.show_info(f"Creating and switching to collection '{collection_name}'...")
|
|
21
|
+
success = await app.rag_manager.switch_to_collection(collection_name)
|
|
22
|
+
|
|
23
|
+
if success:
|
|
24
|
+
await app.update_status_bar()
|
|
25
|
+
app.ui.show_success(f"Collection '{collection_name}' created and activated")
|
|
26
|
+
app.ui.show_info("Chat session reset - ready for new conversation")
|
|
27
|
+
else:
|
|
28
|
+
app.ui.show_error("Failed to create collection")
|
|
29
|
+
else:
|
|
30
|
+
app.ui.show_error("Collection name is required")
|
|
31
|
+
|
|
32
|
+
return True
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
async def handle_new_chat(args: str) -> bool:
|
|
36
|
+
"""Create a new chat session."""
|
|
37
|
+
app = get_app_state()
|
|
38
|
+
|
|
39
|
+
if not app.rag_manager.connected:
|
|
40
|
+
app.ui.show_error("Not connected to H2OGPTE. Use /register first.")
|
|
41
|
+
return True
|
|
42
|
+
|
|
43
|
+
# Ensure we have a collection first
|
|
44
|
+
if not await app.rag_manager.get_collection_name():
|
|
45
|
+
app.ui.show_info("No active collection. Creating default collection...")
|
|
46
|
+
success = await app.rag_manager.switch_to_collection("CLI-Collection")
|
|
47
|
+
if not success:
|
|
48
|
+
app.ui.show_error("Failed to create collection")
|
|
49
|
+
return True
|
|
50
|
+
|
|
51
|
+
if not args:
|
|
52
|
+
import datetime
|
|
53
|
+
|
|
54
|
+
session_name = f"chat-{datetime.datetime.now().strftime('%Y%m%d-%H%M%S')}"
|
|
55
|
+
else:
|
|
56
|
+
session_name = args.strip()
|
|
57
|
+
|
|
58
|
+
app.ui.show_info(f"Creating new chat session '{session_name}'...")
|
|
59
|
+
success = await app.rag_manager.create_chat_session(session_name)
|
|
60
|
+
|
|
61
|
+
if success:
|
|
62
|
+
await app.update_status_bar()
|
|
63
|
+
app.ui.show_success(f"Chat session '{session_name}' created and activated")
|
|
64
|
+
else:
|
|
65
|
+
app.ui.show_error("Failed to create chat session")
|
|
66
|
+
|
|
67
|
+
return True
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from rich.table import Table
|
|
2
|
+
|
|
3
|
+
from ...core.app import get_app_state
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def handle_config(args: str) -> bool:
|
|
7
|
+
"""Configure settings."""
|
|
8
|
+
app = get_app_state()
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
categories = ["RAG", "Agent", "UI", "View Current", "Save & Exit"]
|
|
12
|
+
choice = app.ui.prompt.select_from_list(
|
|
13
|
+
categories, "Select configuration category:"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
if choice == "RAG":
|
|
17
|
+
app.console.print("[cyan]RAG Configuration[/cyan]")
|
|
18
|
+
|
|
19
|
+
current_endpoint = app.settings.rag.endpoint or "not set"
|
|
20
|
+
new_endpoint = app.ui.prompt.get_input(
|
|
21
|
+
f"RAG Endpoint [{current_endpoint}]: "
|
|
22
|
+
)
|
|
23
|
+
if new_endpoint:
|
|
24
|
+
app.settings.rag.endpoint = new_endpoint
|
|
25
|
+
|
|
26
|
+
current_key = "*" * 8 if app.settings.rag.api_key else "not set"
|
|
27
|
+
new_key = app.ui.prompt.get_input(f"API Key [{current_key}]: ")
|
|
28
|
+
if new_key:
|
|
29
|
+
app.settings.rag.api_key = new_key
|
|
30
|
+
|
|
31
|
+
current_collection = app.settings.rag.collection_name
|
|
32
|
+
new_collection = app.ui.prompt.get_input(
|
|
33
|
+
f"Collection [{current_collection}]: "
|
|
34
|
+
)
|
|
35
|
+
if new_collection:
|
|
36
|
+
app.settings.rag.collection_name = new_collection
|
|
37
|
+
|
|
38
|
+
elif choice == "Agent":
|
|
39
|
+
app.console.print("[cyan]Agent Configuration[/cyan]")
|
|
40
|
+
|
|
41
|
+
current_endpoint = app.settings.agent.endpoint or "not set"
|
|
42
|
+
new_endpoint = app.ui.prompt.get_input(
|
|
43
|
+
f"Agent Endpoint [{current_endpoint}]: "
|
|
44
|
+
)
|
|
45
|
+
if new_endpoint:
|
|
46
|
+
app.settings.agent.endpoint = new_endpoint
|
|
47
|
+
|
|
48
|
+
current_key = "*" * 8 if app.settings.agent.api_key else "not set"
|
|
49
|
+
new_key = app.ui.prompt.get_input(f"API Key [{current_key}]: ")
|
|
50
|
+
if new_key:
|
|
51
|
+
app.settings.agent.api_key = new_key
|
|
52
|
+
|
|
53
|
+
current_model = app.settings.agent.model
|
|
54
|
+
new_model = app.ui.prompt.get_input(f"Model [{current_model}]: ")
|
|
55
|
+
if new_model:
|
|
56
|
+
app.settings.agent.model = new_model
|
|
57
|
+
|
|
58
|
+
elif choice == "UI":
|
|
59
|
+
app.console.print("[cyan]UI Configuration[/cyan]")
|
|
60
|
+
|
|
61
|
+
current_theme = app.settings.ui.theme
|
|
62
|
+
new_theme = app.ui.prompt.get_input(f"Theme [{current_theme}]: ")
|
|
63
|
+
if new_theme:
|
|
64
|
+
app.settings.ui.theme = new_theme
|
|
65
|
+
|
|
66
|
+
app.settings.ui.show_progress = app.ui.prompt.confirm(
|
|
67
|
+
"Show progress bars", default=app.settings.ui.show_progress
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
app.settings.ui.animation = app.ui.prompt.confirm(
|
|
71
|
+
"Enable animations", default=app.settings.ui.animation
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
elif choice == "View Current":
|
|
75
|
+
_show_current_config(app)
|
|
76
|
+
|
|
77
|
+
if choice not in ["View Current", "Save & Exit"]:
|
|
78
|
+
try:
|
|
79
|
+
app.settings.save()
|
|
80
|
+
app.ui.show_success("Configuration saved")
|
|
81
|
+
# Update status bar after config changes
|
|
82
|
+
app.update_status_bar()
|
|
83
|
+
except Exception as e:
|
|
84
|
+
app.ui.show_error(f"Failed to save configuration: {e}")
|
|
85
|
+
|
|
86
|
+
except Exception as e:
|
|
87
|
+
app.ui.show_error(f"Configuration error: {e}")
|
|
88
|
+
|
|
89
|
+
return True
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _show_current_config(app):
|
|
93
|
+
"""Show current configuration."""
|
|
94
|
+
table = Table(title="Current Configuration")
|
|
95
|
+
table.add_column("Setting", style="cyan")
|
|
96
|
+
table.add_column("Value", style="white")
|
|
97
|
+
|
|
98
|
+
# RAG settings
|
|
99
|
+
table.add_row("RAG Endpoint", app.settings.rag.endpoint or "not set")
|
|
100
|
+
table.add_row("RAG API Key", "*" * 8 if app.settings.rag.api_key else "not set")
|
|
101
|
+
table.add_row("RAG Collection", app.settings.rag.collection_name)
|
|
102
|
+
|
|
103
|
+
# Agent settings
|
|
104
|
+
table.add_row("Agent Endpoint", app.settings.agent.endpoint or "not set")
|
|
105
|
+
table.add_row("Agent API Key", "*" * 8 if app.settings.agent.api_key else "not set")
|
|
106
|
+
table.add_row("Agent Model", app.settings.agent.model)
|
|
107
|
+
|
|
108
|
+
# UI settings
|
|
109
|
+
table.add_row("UI Theme", app.settings.ui.theme)
|
|
110
|
+
table.add_row("Show Progress", str(app.settings.ui.show_progress))
|
|
111
|
+
table.add_row("Animations", str(app.settings.ui.animation))
|
|
112
|
+
|
|
113
|
+
app.console.print(table)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from ...core.app import get_app_state
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
async def handle_disconnect(args: str) -> bool:
|
|
5
|
+
"""Disconnect from H2OGPTE and clear saved credentials."""
|
|
6
|
+
app = get_app_state()
|
|
7
|
+
|
|
8
|
+
if not app.rag_manager.connected:
|
|
9
|
+
app.ui.show_info("Already disconnected from H2OGPTE")
|
|
10
|
+
return True
|
|
11
|
+
|
|
12
|
+
# Confirm disconnection
|
|
13
|
+
if not app.ui.prompt.confirm(
|
|
14
|
+
"Disconnect and clear saved credentials?", default=False
|
|
15
|
+
):
|
|
16
|
+
app.ui.show_info("Disconnection cancelled")
|
|
17
|
+
return True
|
|
18
|
+
|
|
19
|
+
# Disconnect from RAG manager
|
|
20
|
+
await app.rag_manager.close()
|
|
21
|
+
|
|
22
|
+
# Clear credentials from settings
|
|
23
|
+
app.settings.rag.endpoint = ""
|
|
24
|
+
app.settings.rag.api_key = ""
|
|
25
|
+
app.settings.rag.collection_name = "default"
|
|
26
|
+
|
|
27
|
+
# Save cleared settings
|
|
28
|
+
app.settings.save()
|
|
29
|
+
|
|
30
|
+
# Update status bar
|
|
31
|
+
await app.update_status_bar()
|
|
32
|
+
|
|
33
|
+
app.ui.show_success("Disconnected and credentials cleared")
|
|
34
|
+
app.ui.show_info("Use /register to connect again")
|
|
35
|
+
|
|
36
|
+
return True
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from rich.panel import Panel
|
|
2
|
+
|
|
3
|
+
from ...core.app import get_app_state
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def handle_exit(args: str) -> bool:
|
|
7
|
+
"""Exit the application."""
|
|
8
|
+
app = get_app_state()
|
|
9
|
+
|
|
10
|
+
# Check if user provided 'y' as an argument for immediate exit
|
|
11
|
+
if args and args.strip().lower() in ["y", "yes"]:
|
|
12
|
+
# Exit immediately without confirmation
|
|
13
|
+
await app.cleanup()
|
|
14
|
+
|
|
15
|
+
app.console.print(
|
|
16
|
+
Panel(
|
|
17
|
+
"[bold cyan]Thank you for using H2OGPTE CLI![/bold cyan]\n"
|
|
18
|
+
"[dim]Session saved automatically[/dim]",
|
|
19
|
+
border_style="cyan",
|
|
20
|
+
)
|
|
21
|
+
)
|
|
22
|
+
return False
|
|
23
|
+
|
|
24
|
+
# Otherwise, ask for confirmation
|
|
25
|
+
if app.ui.prompt.confirm("Are you sure you want to exit?", default=False):
|
|
26
|
+
# Clean up
|
|
27
|
+
await app.cleanup()
|
|
28
|
+
|
|
29
|
+
app.console.print(
|
|
30
|
+
Panel(
|
|
31
|
+
"[bold cyan]Thank you for using H2OGPTE CLI![/bold cyan]\n"
|
|
32
|
+
"[dim]Session saved automatically[/dim]",
|
|
33
|
+
border_style="cyan",
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
return False
|
|
37
|
+
return True
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from rich.table import Table
|
|
3
|
+
|
|
4
|
+
from ...core.app import get_app_state
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
async def handle_history(args: str) -> bool:
|
|
8
|
+
"""Show command history."""
|
|
9
|
+
app = get_app_state()
|
|
10
|
+
limit = int(args) if args.isdigit() else 20
|
|
11
|
+
|
|
12
|
+
history = app.session.history[-limit:]
|
|
13
|
+
if not history:
|
|
14
|
+
app.ui.show_info("No command history")
|
|
15
|
+
return True
|
|
16
|
+
|
|
17
|
+
table = Table(title=f"Command History (last {len(history)} entries)")
|
|
18
|
+
table.add_column("#", style="dim")
|
|
19
|
+
table.add_column("Time", style="cyan")
|
|
20
|
+
table.add_column("Command", style="white")
|
|
21
|
+
table.add_column("Status", style="white")
|
|
22
|
+
|
|
23
|
+
for i, entry in enumerate(history, 1):
|
|
24
|
+
time_str = datetime.fromisoformat(entry["timestamp"]).strftime("%H:%M:%S")
|
|
25
|
+
status = "[green]✓[/green]" if entry["success"] else "[red]✗[/red]"
|
|
26
|
+
table.add_row(str(i), time_str, entry["command"][:50], status)
|
|
27
|
+
|
|
28
|
+
app.console.print(table)
|
|
29
|
+
return True
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from ...core.app import get_app_state
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def handle_register(args: str) -> bool:
|
|
7
|
+
"""Register H2OGPTE system."""
|
|
8
|
+
app = get_app_state()
|
|
9
|
+
|
|
10
|
+
# Parse arguments or prompt for them
|
|
11
|
+
parts = args.split()
|
|
12
|
+
|
|
13
|
+
if len(parts) < 2:
|
|
14
|
+
# Check if user explicitly wants to clear credentials (special keyword)
|
|
15
|
+
if args.strip().lower() in ["clear", "disconnect", "reset"]:
|
|
16
|
+
app.ui.show_info("Clearing credentials...")
|
|
17
|
+
# Clear existing connection
|
|
18
|
+
if app.rag_manager.connected:
|
|
19
|
+
await app.rag_manager.close()
|
|
20
|
+
|
|
21
|
+
# Clear credentials
|
|
22
|
+
app.settings.rag.endpoint = ""
|
|
23
|
+
app.settings.rag.api_key = ""
|
|
24
|
+
app.settings.rag.collection_name = "default"
|
|
25
|
+
app.settings.save()
|
|
26
|
+
|
|
27
|
+
# Update status bar
|
|
28
|
+
await app.update_status_bar()
|
|
29
|
+
|
|
30
|
+
app.ui.show_success(
|
|
31
|
+
"Credentials cleared. Use /register <address> <api_key> to reconnect"
|
|
32
|
+
)
|
|
33
|
+
return True
|
|
34
|
+
|
|
35
|
+
# Interactive mode with defaults and secure input
|
|
36
|
+
address = app.ui.prompt.get_input_with_default(
|
|
37
|
+
"H2OGPTE Address", "https://h2ogpte.genai.h2o.ai"
|
|
38
|
+
)
|
|
39
|
+
api_key = app.ui.prompt.get_secret("API Key")
|
|
40
|
+
|
|
41
|
+
# If user cancels input, don't proceed
|
|
42
|
+
if not address or not api_key:
|
|
43
|
+
app.ui.show_info("Registration cancelled")
|
|
44
|
+
return True
|
|
45
|
+
else:
|
|
46
|
+
address = parts[0]
|
|
47
|
+
api_key = parts[1]
|
|
48
|
+
|
|
49
|
+
# Step 1: Connect and get username only
|
|
50
|
+
app.ui.show_info("Step 1: Connecting and retrieving user information...")
|
|
51
|
+
|
|
52
|
+
# Save to settings with encrypted API key
|
|
53
|
+
app.settings.rag.endpoint = address
|
|
54
|
+
app.settings.set_rag_api_key(api_key)
|
|
55
|
+
|
|
56
|
+
# Try to connect and get username (without creating collection yet)
|
|
57
|
+
success = await app.rag_manager.connect_and_get_user(address, api_key)
|
|
58
|
+
|
|
59
|
+
if success:
|
|
60
|
+
# Get username and update status bar
|
|
61
|
+
username = await app.rag_manager.get_username()
|
|
62
|
+
if username:
|
|
63
|
+
app.ui.show_success(f"Connected as user: {username}")
|
|
64
|
+
|
|
65
|
+
# Save settings on successful connection
|
|
66
|
+
app.settings.save()
|
|
67
|
+
|
|
68
|
+
# Update status bar with username
|
|
69
|
+
await app.update_status_bar()
|
|
70
|
+
app.ui.show_success("Connected successfully! Ready to chat.")
|
|
71
|
+
|
|
72
|
+
else:
|
|
73
|
+
app.ui.show_error("Connected but failed to get username")
|
|
74
|
+
else:
|
|
75
|
+
app.ui.show_error("Failed to connect to H2OGPTE")
|
|
76
|
+
|
|
77
|
+
return True
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
async def handle_upload(args: str) -> bool:
|
|
81
|
+
"""Upload files to RAG."""
|
|
82
|
+
app = get_app_state()
|
|
83
|
+
|
|
84
|
+
if not args:
|
|
85
|
+
# Interactive file selection
|
|
86
|
+
path = app.ui.prompt.get_path("Select file or directory to upload: ")
|
|
87
|
+
if not path:
|
|
88
|
+
return True
|
|
89
|
+
else:
|
|
90
|
+
path = Path(args.strip())
|
|
91
|
+
|
|
92
|
+
if not path.exists():
|
|
93
|
+
app.ui.show_error(f"Path not found: {path}")
|
|
94
|
+
return True
|
|
95
|
+
|
|
96
|
+
# Collect files
|
|
97
|
+
if path.is_file():
|
|
98
|
+
files = [path]
|
|
99
|
+
else:
|
|
100
|
+
# Scan directory
|
|
101
|
+
patterns = app.ui.prompt.get_multiselect(
|
|
102
|
+
["*.py", "*.js", "*.txt", "*.md", "*.json", "*"],
|
|
103
|
+
"Select file patterns to include:",
|
|
104
|
+
)
|
|
105
|
+
files = app.file_manager.scan_directory(
|
|
106
|
+
path, patterns if patterns != ["*"] else None
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
if not files:
|
|
110
|
+
app.ui.show_warning("No files to upload")
|
|
111
|
+
return True
|
|
112
|
+
|
|
113
|
+
# Display files to upload
|
|
114
|
+
app.file_manager.display_file_list(files, show_details=False)
|
|
115
|
+
|
|
116
|
+
if not app.ui.prompt.confirm(f"Upload {len(files)} file(s)?", default=True):
|
|
117
|
+
return True
|
|
118
|
+
|
|
119
|
+
# Upload files
|
|
120
|
+
await app.rag_manager.upload_files(files)
|
|
121
|
+
return True
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
async def handle_analyze(args: str) -> bool:
|
|
125
|
+
"""Analyze directory."""
|
|
126
|
+
app = get_app_state()
|
|
127
|
+
|
|
128
|
+
if not args:
|
|
129
|
+
path = Path.cwd()
|
|
130
|
+
else:
|
|
131
|
+
path = Path(args.strip())
|
|
132
|
+
|
|
133
|
+
if not path.exists() or not path.is_dir():
|
|
134
|
+
app.ui.show_error(f"Invalid directory: {path}")
|
|
135
|
+
return True
|
|
136
|
+
|
|
137
|
+
# Analyze directory
|
|
138
|
+
analysis = await app.dir_analyzer.analyze(path)
|
|
139
|
+
|
|
140
|
+
# Ask if user wants to upload
|
|
141
|
+
if analysis["total_files"] > 0:
|
|
142
|
+
if app.ui.prompt.confirm("Upload directory contents to RAG?"):
|
|
143
|
+
files = app.file_manager.scan_directory(path)
|
|
144
|
+
await app.rag_manager.upload_files(files)
|
|
145
|
+
|
|
146
|
+
return True
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from ...core.app import get_app_state
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
async def handle_research_agent(args: str) -> bool:
|
|
5
|
+
app = get_app_state()
|
|
6
|
+
|
|
7
|
+
if not args or not args.strip():
|
|
8
|
+
app.ui.show_error("Please provide a research query")
|
|
9
|
+
app.ui.show_info(
|
|
10
|
+
"Example: /research What are the latest developments in quantum computing?"
|
|
11
|
+
)
|
|
12
|
+
return True
|
|
13
|
+
|
|
14
|
+
message = args.strip()
|
|
15
|
+
|
|
16
|
+
if app.rag_manager.connected:
|
|
17
|
+
usage_stats = await app.rag_manager.send_message(
|
|
18
|
+
message, use_agent=True, agent_type="deep_research"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
if usage_stats:
|
|
22
|
+
total_input_tokens = sum(
|
|
23
|
+
u.get("input_tokens", 0) for u in usage_stats.get("usage", [])
|
|
24
|
+
)
|
|
25
|
+
total_output_tokens = sum(
|
|
26
|
+
u.get("output_tokens", 0) for u in usage_stats.get("usage", [])
|
|
27
|
+
)
|
|
28
|
+
response_time = usage_stats.get("response_time", "N/A")
|
|
29
|
+
queued_for = usage_stats.get("queue_time", "N/A")
|
|
30
|
+
llm = usage_stats.get("llm", "N/A")
|
|
31
|
+
app.ui.show_info(
|
|
32
|
+
f"[Research] Tokens: {total_input_tokens} (in), {total_output_tokens} (out), "
|
|
33
|
+
f"Response time: {response_time}, "
|
|
34
|
+
f"Queued for: {queued_for}, "
|
|
35
|
+
f"LLM: {llm}"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
await app.update_status_bar()
|
|
39
|
+
else:
|
|
40
|
+
app.ui.show_error("Not connected to H2OGPTE. Use /register to connect first.")
|
|
41
|
+
app.ui.show_info(
|
|
42
|
+
"Example: /register https://your-h2ogpte-endpoint.com your-api-key"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
return True
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import datetime
|
|
3
|
+
|
|
4
|
+
from ...core.app import get_app_state
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
async def handle_session(args: str) -> bool:
|
|
8
|
+
"""Create a new chat session with optional name."""
|
|
9
|
+
app = get_app_state()
|
|
10
|
+
|
|
11
|
+
if not app.rag_manager.connected:
|
|
12
|
+
app.ui.show_error("Not connected to H2OGPTE. Use /register first.")
|
|
13
|
+
return True
|
|
14
|
+
|
|
15
|
+
# Ensure we have a collection first
|
|
16
|
+
if not await app.rag_manager.get_collection_name():
|
|
17
|
+
app.ui.show_info("No active collection. Creating default collection...")
|
|
18
|
+
success = await app.rag_manager.switch_to_collection("CLI-Collection")
|
|
19
|
+
if not success:
|
|
20
|
+
app.ui.show_error("Failed to create collection")
|
|
21
|
+
return True
|
|
22
|
+
|
|
23
|
+
# Determine session name
|
|
24
|
+
if not args:
|
|
25
|
+
session_name = f"chat-{datetime.datetime.now().strftime('%Y%m%d-%H%M%S')}"
|
|
26
|
+
else:
|
|
27
|
+
session_name = args.strip()
|
|
28
|
+
|
|
29
|
+
app.ui.show_info(f"Creating new chat session '{session_name}'...")
|
|
30
|
+
|
|
31
|
+
# Create the chat session and get the session ID
|
|
32
|
+
session_id = await app.rag_manager.create_chat_session_with_name(session_name)
|
|
33
|
+
|
|
34
|
+
if session_id:
|
|
35
|
+
await app.update_status_bar()
|
|
36
|
+
app.ui.show_success(f"Chat session '{session_name}' created and activated")
|
|
37
|
+
else:
|
|
38
|
+
app.ui.show_error("Failed to create chat session")
|
|
39
|
+
|
|
40
|
+
return True
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
async def handle_save(args: str) -> bool:
|
|
44
|
+
"""Save session."""
|
|
45
|
+
app = get_app_state()
|
|
46
|
+
|
|
47
|
+
if args:
|
|
48
|
+
path = Path(args)
|
|
49
|
+
else:
|
|
50
|
+
path = None
|
|
51
|
+
|
|
52
|
+
app.session.save_session(path)
|
|
53
|
+
return True
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
async def handle_load(args: str) -> bool:
|
|
57
|
+
"""Load session."""
|
|
58
|
+
app = get_app_state()
|
|
59
|
+
|
|
60
|
+
if not args:
|
|
61
|
+
app.ui.show_error("Please provide a session file path")
|
|
62
|
+
return True
|
|
63
|
+
|
|
64
|
+
path = Path(args)
|
|
65
|
+
if not path.exists():
|
|
66
|
+
app.ui.show_error(f"Session file not found: {path}")
|
|
67
|
+
return True
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
from ...core.session import Session
|
|
71
|
+
|
|
72
|
+
app.session = Session.load_session(path)
|
|
73
|
+
app.ui.show_success(f"Session loaded from {path}")
|
|
74
|
+
except Exception as e:
|
|
75
|
+
app.ui.show_error(f"Failed to load session: {e}")
|
|
76
|
+
|
|
77
|
+
return True
|