RC version
This commit is contained in:
@@ -152,6 +152,7 @@ testremote/
|
||||
*.db
|
||||
*.patch
|
||||
scratch.py
|
||||
connpy.code-workspace
|
||||
|
||||
# Internal planning and implementation docs
|
||||
PLAN_CAPA_SERVICIOS.md
|
||||
|
||||
@@ -17,8 +17,12 @@ The v6 release introduces the **AI Copilot**, an interactive terminal assistant
|
||||
## 🤖 AI Copilot (New in v6)
|
||||
The AI Copilot is deeply integrated into your terminal workflow:
|
||||
- **Terminal Context Awareness**: The Copilot can "see" your screen output, helping you diagnose errors or analyze command results in real-time.
|
||||
- **Dynamic Context Selection**: Flexibly select single, range, or line-based terminal blocks to feed the Copilot, filtering out interactive scrolling garbage automatically (e.g., Cisco IOS/XR scrolling, paginators).
|
||||
- **Hybrid Multi-Agent System**: Automatically escalates complex tasks between the **Network Engineer** (execution) and the **Network Architect** (strategy).
|
||||
- **MCP Integration**: Dynamically load tools from external providers (6WIND, AWS, etc.) via the Model Context Protocol.
|
||||
- **Flexible Auth & Keyless AI**: Support for advanced LiteLLM credentials (`--engineer-auth` / `--architect-auth`) allowing keyless local models (Ollama), cloud engines (Vertex AI), or custom endpoints.
|
||||
- **Enhanced Session Management**: Uniquely generated sessions, robust pagination, and interactive styling translating prompt themes directly to terminal escapes.
|
||||
- **Semantic Prompt Integration**: Emit standard OSC prompt sequences (`\x1b]133;B`) for real-time remote/web front-end command tracking.
|
||||
- **Interactive Chat**: Launch with `conn ai` for a collaborative troubleshooting session.
|
||||
|
||||
|
||||
|
||||
+7
-1
@@ -19,8 +19,12 @@ The v6 release introduces the **AI Copilot**, an interactive terminal assistant
|
||||
## 🤖 AI Copilot (New in v6)
|
||||
The AI Copilot is deeply integrated into your terminal workflow:
|
||||
- **Terminal Context Awareness**: The Copilot can "see" your screen output, helping you diagnose errors or analyze command results in real-time.
|
||||
- **Dynamic Context Selection**: Flexibly select single, range, or line-based terminal blocks to feed the Copilot, filtering out interactive scrolling garbage automatically (e.g., Cisco IOS/XR scrolling, paginators).
|
||||
- **Hybrid Multi-Agent System**: Automatically escalates complex tasks between the **Network Engineer** (execution) and the **Network Architect** (strategy).
|
||||
- **MCP Integration**: Dynamically load tools from external providers (6WIND, AWS, etc.) via the Model Context Protocol.
|
||||
- **Flexible Auth & Keyless AI**: Support for advanced LiteLLM credentials (`--engineer-auth` / `--architect-auth`) allowing keyless local models (Ollama), cloud engines (Vertex AI), or custom endpoints.
|
||||
- **Enhanced Session Management**: Uniquely generated sessions, robust pagination, and interactive styling translating prompt themes directly to terminal escapes.
|
||||
- **Semantic Prompt Integration**: Emit standard OSC prompt sequences (`\x1b]133;B`) for real-time remote/web front-end command tracking.
|
||||
- **Interactive Chat**: Launch with `conn ai` for a collaborative troubleshooting session.
|
||||
|
||||
|
||||
@@ -203,5 +207,7 @@ __pdoc__ = {
|
||||
'nodes.deferred_class_hooks': False,
|
||||
'connapp': False,
|
||||
'connapp.encrypt': True,
|
||||
'printer': False
|
||||
'printer': False,
|
||||
'tests': False
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__ = "6.0.0b11"
|
||||
__version__ = "6.0.0b12"
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import pytest
|
||||
from connpy.utils import log_cleaner
|
||||
|
||||
def test_log_cleaner_empty():
|
||||
assert log_cleaner("") == ""
|
||||
assert log_cleaner(None) == ""
|
||||
|
||||
def test_log_cleaner_plain_text():
|
||||
assert log_cleaner("hello world") == "hello world"
|
||||
|
||||
def test_log_cleaner_ansi_colors():
|
||||
# \x1b[31m is red, \x1b[0m is reset
|
||||
assert log_cleaner("\x1b[31mhello\x1b[0m world") == "hello world"
|
||||
|
||||
def test_log_cleaner_osc_window_title():
|
||||
# Set window title OSC: \x1b]0;my title\x07 followed by prompt
|
||||
sample = "\x1b]0;fluzzi32@norman: ~\x07fluzzi32@norman:~$"
|
||||
assert log_cleaner(sample) == "fluzzi32@norman:~$"
|
||||
|
||||
def test_log_cleaner_osc_with_st_terminator():
|
||||
# OSC can also be terminated by \x1b\\ (ST)
|
||||
sample = "\x1b]0;some title\x1b\\my_prompt>"
|
||||
assert log_cleaner(sample) == "my_prompt>"
|
||||
|
||||
def test_log_cleaner_mixed_ansi_and_osc():
|
||||
sample = "\x1b]0;title\x07\x1b[32muser@host\x1b[0m:\x1b[34m/path\x1b[0m$ "
|
||||
assert log_cleaner(sample) == "user@host:/path$"
|
||||
|
||||
def test_log_cleaner_carriage_return_and_backspace():
|
||||
# Test that standard control sequences like \r and \b still work as expected
|
||||
assert log_cleaner("hello\rworld") == "world"
|
||||
assert log_cleaner("hell\bo") == "helo"
|
||||
@@ -7,6 +7,9 @@ def log_cleaner(data: str) -> str:
|
||||
if not data:
|
||||
return ""
|
||||
|
||||
# Remove OSC (Operating System Command) sequences (e.g., set window title \x1b]0;...\x07)
|
||||
data = re.sub(r'\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)', '', data)
|
||||
|
||||
lines = data.split('\n')
|
||||
cleaned_lines = []
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.cli.ai_handler API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -61,13 +61,22 @@ el.replaceWith(d);
|
||||
|
||||
def dispatch(self, args):
|
||||
if args.list_sessions:
|
||||
sessions = self.app.services.ai.list_sessions()
|
||||
limit = 20 if not getattr(args, "all", False) else None
|
||||
sessions, total = self.app.services.ai.list_sessions(limit=limit)
|
||||
if not sessions:
|
||||
printer.info("No saved AI sessions found.")
|
||||
return
|
||||
|
||||
columns = ["ID", "Title", "Created At", "Model"]
|
||||
rows = [[s["id"], s["title"], s["created_at"], s["model"]] for s in sessions]
|
||||
printer.table("AI Persisted Sessions", columns, rows)
|
||||
|
||||
title = "AI Persisted Sessions"
|
||||
if limit and total > limit:
|
||||
title += f" (Showing last {limit} of {total})"
|
||||
|
||||
printer.table(title, columns, rows)
|
||||
if limit and total > limit:
|
||||
printer.info(f"Use '--list --all' to see all {total} sessions.")
|
||||
return
|
||||
|
||||
if args.delete_session:
|
||||
@@ -84,7 +93,7 @@ el.replaceWith(d);
|
||||
# Determinar session_id para retomar
|
||||
session_id = None
|
||||
if args.resume:
|
||||
sessions = self.app.services.ai.list_sessions()
|
||||
sessions, _ = self.app.services.ai.list_sessions()
|
||||
session_id = sessions[0]["id"] if sessions else None
|
||||
if not session_id:
|
||||
printer.warning("No previous session found to resume.")
|
||||
@@ -102,16 +111,23 @@ el.replaceWith(d);
|
||||
arguments[key] = cli_val[0]
|
||||
elif settings.get(key):
|
||||
arguments[key] = settings.get(key)
|
||||
|
||||
for key in ["engineer_auth", "architect_auth"]:
|
||||
cli_val = getattr(args, key, None)
|
||||
if cli_val:
|
||||
arguments[key] = self._parse_auth_value(cli_val[0])
|
||||
elif settings.get(key):
|
||||
arguments[key] = settings.get(key)
|
||||
|
||||
# Check keys only if running in local mode (not remote)
|
||||
if getattr(self.app.services, "mode", "local") == "local":
|
||||
if not arguments.get("engineer_api_key"):
|
||||
printer.error("Engineer API key not configured. The chat cannot start.")
|
||||
printer.info("Use 'connpy config --engineer-api-key <key>' to set it.")
|
||||
if not arguments.get("engineer_api_key") and not arguments.get("engineer_auth"):
|
||||
printer.error("Engineer API key/auth not configured. The chat cannot start.")
|
||||
printer.info("Use 'connpy config --engineer-api-key <key>' or 'connpy config --engineer-auth <auth>' to set it.")
|
||||
sys.exit(1)
|
||||
if not arguments.get("architect_api_key"):
|
||||
printer.warning("Architect API key not configured. Architect will be unavailable.")
|
||||
printer.info("Use 'connpy config --architect-api-key <key>' to enable it.")
|
||||
if not arguments.get("architect_api_key") and not arguments.get("architect_auth"):
|
||||
printer.warning("Architect API key/auth not configured. Architect will be unavailable.")
|
||||
printer.info("Use 'connpy config --architect-api-key <key>' or 'connpy config --architect-auth <auth>' to enable it.")
|
||||
|
||||
# El resto de la interacción el CLI la maneja con el agente subyacente
|
||||
self.app.myai = self.app.services.ai
|
||||
@@ -148,7 +164,7 @@ el.replaceWith(d);
|
||||
if history:
|
||||
mdprint(f"[debug]Analyzing {len(history)} previous messages...[/debug]\n")
|
||||
else:
|
||||
printer.error(f"Could not load session {session_id}. Starting clean.")
|
||||
printer.info(f"Session '{session_id}' not found. Starting clean.")
|
||||
|
||||
if not history:
|
||||
mdprint(Rule(style="engineer"))
|
||||
@@ -162,7 +178,7 @@ el.replaceWith(d);
|
||||
if user_query.lower() in ['exit', 'quit', 'bye', 'cancel']: break
|
||||
|
||||
with console.status("[ai_status]Agent is thinking...") as status:
|
||||
result = self.app.myai.ask(user_query, chat_history=history, status=status, debug=args.debug, trust=args.trust, **self.ai_overrides)
|
||||
result = self.app.myai.ask(user_query, chat_history=history, status=status, debug=args.debug, trust=args.trust, session_id=session_id, **self.ai_overrides)
|
||||
|
||||
new_history = result.get("chat_history")
|
||||
if new_history is not None:
|
||||
@@ -193,8 +209,7 @@ el.replaceWith(d);
|
||||
action = mcp_args[0].lower()
|
||||
|
||||
if action == "list":
|
||||
settings = self.app.services.config_svc.get_settings()
|
||||
mcp_servers = settings.get("ai", {}).get("mcp_servers", {})
|
||||
mcp_servers = self.app.services.ai.list_mcp_servers()
|
||||
if not mcp_servers:
|
||||
printer.info("No MCP servers configured.")
|
||||
else:
|
||||
@@ -259,8 +274,7 @@ el.replaceWith(d);
|
||||
from .forms import Forms
|
||||
self.app.cli_forms = Forms(self.app)
|
||||
|
||||
settings = self.app.services.config_svc.get_settings()
|
||||
mcp_servers = settings.get("ai", {}).get("mcp_servers", {})
|
||||
mcp_servers = self.app.services.ai.list_mcp_servers()
|
||||
|
||||
result = self.app.cli_forms.mcp_wizard(mcp_servers)
|
||||
if not result:
|
||||
@@ -294,7 +308,37 @@ el.replaceWith(d);
|
||||
printer.success(f"MCP server '{result['name']}' removed.")
|
||||
|
||||
except Exception as e:
|
||||
printer.error(str(e))</code></pre>
|
||||
printer.error(str(e))
|
||||
|
||||
def _parse_auth_value(self, value):
|
||||
if not value or value.lower() in ["none", "clear"]:
|
||||
return None
|
||||
import os
|
||||
import yaml
|
||||
import json
|
||||
if os.path.exists(value):
|
||||
try:
|
||||
with open(value, "r") as f:
|
||||
content = f.read()
|
||||
try:
|
||||
return json.loads(content)
|
||||
except ValueError:
|
||||
return yaml.safe_load(content)
|
||||
except Exception as e:
|
||||
printer.error(f"Failed to read/parse auth file '{value}': {e}")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
return json.loads(value)
|
||||
except ValueError:
|
||||
try:
|
||||
parsed = yaml.safe_load(value)
|
||||
if isinstance(parsed, dict):
|
||||
return parsed
|
||||
raise ValueError()
|
||||
except Exception:
|
||||
printer.error("Auth parameter must be a valid JSON/YAML string, or a path to a JSON/YAML file.")
|
||||
sys.exit(1)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
<h3>Methods</h3>
|
||||
@@ -316,8 +360,7 @@ el.replaceWith(d);
|
||||
action = mcp_args[0].lower()
|
||||
|
||||
if action == "list":
|
||||
settings = self.app.services.config_svc.get_settings()
|
||||
mcp_servers = settings.get("ai", {}).get("mcp_servers", {})
|
||||
mcp_servers = self.app.services.ai.list_mcp_servers()
|
||||
if not mcp_servers:
|
||||
printer.info("No MCP servers configured.")
|
||||
else:
|
||||
@@ -382,8 +425,7 @@ el.replaceWith(d);
|
||||
from .forms import Forms
|
||||
self.app.cli_forms = Forms(self.app)
|
||||
|
||||
settings = self.app.services.config_svc.get_settings()
|
||||
mcp_servers = settings.get("ai", {}).get("mcp_servers", {})
|
||||
mcp_servers = self.app.services.ai.list_mcp_servers()
|
||||
|
||||
result = self.app.cli_forms.mcp_wizard(mcp_servers)
|
||||
if not result:
|
||||
@@ -431,13 +473,22 @@ el.replaceWith(d);
|
||||
</summary>
|
||||
<pre><code class="python">def dispatch(self, args):
|
||||
if args.list_sessions:
|
||||
sessions = self.app.services.ai.list_sessions()
|
||||
limit = 20 if not getattr(args, "all", False) else None
|
||||
sessions, total = self.app.services.ai.list_sessions(limit=limit)
|
||||
if not sessions:
|
||||
printer.info("No saved AI sessions found.")
|
||||
return
|
||||
|
||||
columns = ["ID", "Title", "Created At", "Model"]
|
||||
rows = [[s["id"], s["title"], s["created_at"], s["model"]] for s in sessions]
|
||||
printer.table("AI Persisted Sessions", columns, rows)
|
||||
|
||||
title = "AI Persisted Sessions"
|
||||
if limit and total > limit:
|
||||
title += f" (Showing last {limit} of {total})"
|
||||
|
||||
printer.table(title, columns, rows)
|
||||
if limit and total > limit:
|
||||
printer.info(f"Use '--list --all' to see all {total} sessions.")
|
||||
return
|
||||
|
||||
if args.delete_session:
|
||||
@@ -454,7 +505,7 @@ el.replaceWith(d);
|
||||
# Determinar session_id para retomar
|
||||
session_id = None
|
||||
if args.resume:
|
||||
sessions = self.app.services.ai.list_sessions()
|
||||
sessions, _ = self.app.services.ai.list_sessions()
|
||||
session_id = sessions[0]["id"] if sessions else None
|
||||
if not session_id:
|
||||
printer.warning("No previous session found to resume.")
|
||||
@@ -472,16 +523,23 @@ el.replaceWith(d);
|
||||
arguments[key] = cli_val[0]
|
||||
elif settings.get(key):
|
||||
arguments[key] = settings.get(key)
|
||||
|
||||
for key in ["engineer_auth", "architect_auth"]:
|
||||
cli_val = getattr(args, key, None)
|
||||
if cli_val:
|
||||
arguments[key] = self._parse_auth_value(cli_val[0])
|
||||
elif settings.get(key):
|
||||
arguments[key] = settings.get(key)
|
||||
|
||||
# Check keys only if running in local mode (not remote)
|
||||
if getattr(self.app.services, "mode", "local") == "local":
|
||||
if not arguments.get("engineer_api_key"):
|
||||
printer.error("Engineer API key not configured. The chat cannot start.")
|
||||
printer.info("Use 'connpy config --engineer-api-key <key>' to set it.")
|
||||
if not arguments.get("engineer_api_key") and not arguments.get("engineer_auth"):
|
||||
printer.error("Engineer API key/auth not configured. The chat cannot start.")
|
||||
printer.info("Use 'connpy config --engineer-api-key <key>' or 'connpy config --engineer-auth <auth>' to set it.")
|
||||
sys.exit(1)
|
||||
if not arguments.get("architect_api_key"):
|
||||
printer.warning("Architect API key not configured. Architect will be unavailable.")
|
||||
printer.info("Use 'connpy config --architect-api-key <key>' to enable it.")
|
||||
if not arguments.get("architect_api_key") and not arguments.get("architect_auth"):
|
||||
printer.warning("Architect API key/auth not configured. Architect will be unavailable.")
|
||||
printer.info("Use 'connpy config --architect-api-key <key>' or 'connpy config --architect-auth <auth>' to enable it.")
|
||||
|
||||
# El resto de la interacción el CLI la maneja con el agente subyacente
|
||||
self.app.myai = self.app.services.ai
|
||||
@@ -512,7 +570,7 @@ el.replaceWith(d);
|
||||
if history:
|
||||
mdprint(f"[debug]Analyzing {len(history)} previous messages...[/debug]\n")
|
||||
else:
|
||||
printer.error(f"Could not load session {session_id}. Starting clean.")
|
||||
printer.info(f"Session '{session_id}' not found. Starting clean.")
|
||||
|
||||
if not history:
|
||||
mdprint(Rule(style="engineer"))
|
||||
@@ -526,7 +584,7 @@ el.replaceWith(d);
|
||||
if user_query.lower() in ['exit', 'quit', 'bye', 'cancel']: break
|
||||
|
||||
with console.status("[ai_status]Agent is thinking...") as status:
|
||||
result = self.app.myai.ask(user_query, chat_history=history, status=status, debug=args.debug, trust=args.trust, **self.ai_overrides)
|
||||
result = self.app.myai.ask(user_query, chat_history=history, status=status, debug=args.debug, trust=args.trust, session_id=session_id, **self.ai_overrides)
|
||||
|
||||
new_history = result.get("chat_history")
|
||||
if new_history is not None:
|
||||
@@ -608,7 +666,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.cli.api_handler API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -193,7 +193,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.cli.config_handler API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -70,8 +70,10 @@ el.replaceWith(d);
|
||||
"theme": self.set_theme,
|
||||
"engineer_model": self.set_ai_config,
|
||||
"engineer_api_key": self.set_ai_config,
|
||||
"engineer_auth": self.set_ai_config,
|
||||
"architect_model": self.set_ai_config,
|
||||
"architect_api_key": self.set_ai_config,
|
||||
"architect_auth": self.set_ai_config,
|
||||
"trusted_commands": self.set_ai_config,
|
||||
"service_mode": self.set_service_mode,
|
||||
"remote_host": self.set_remote_host,
|
||||
@@ -178,11 +180,59 @@ el.replaceWith(d);
|
||||
try:
|
||||
settings = self.app.services.config_svc.get_settings()
|
||||
aiconfig = settings.get("ai", {})
|
||||
aiconfig[args.command] = args.data[0]
|
||||
val = args.data[0]
|
||||
|
||||
# Check for unset/clear request
|
||||
if val.lower() in ["none", "clear", ""]:
|
||||
if args.command in aiconfig:
|
||||
del aiconfig[args.command]
|
||||
else:
|
||||
# If configuring auth, parse as dictionary (JSON/YAML or file path)
|
||||
if args.command in ["engineer_auth", "architect_auth"]:
|
||||
parsed_val = self._parse_auth_value(val)
|
||||
if parsed_val is not None:
|
||||
aiconfig[args.command] = parsed_val
|
||||
else:
|
||||
if args.command in aiconfig:
|
||||
del aiconfig[args.command]
|
||||
else:
|
||||
aiconfig[args.command] = val
|
||||
|
||||
self.app.services.config_svc.update_setting("ai", aiconfig)
|
||||
printer.success("Config saved")
|
||||
except ConnpyError as e:
|
||||
printer.error(str(e))</code></pre>
|
||||
except (ConnpyError, InvalidConfigurationError) as e:
|
||||
printer.error(str(e))
|
||||
|
||||
def _parse_auth_value(self, value):
|
||||
if value.lower() in ["none", "clear", ""]:
|
||||
return None
|
||||
|
||||
# Check if it's a file path
|
||||
import os
|
||||
if os.path.exists(value):
|
||||
try:
|
||||
with open(value, "r") as f:
|
||||
content = f.read()
|
||||
import json
|
||||
try:
|
||||
return json.loads(content)
|
||||
except ValueError:
|
||||
return yaml.safe_load(content)
|
||||
except Exception as e:
|
||||
raise InvalidConfigurationError(f"Failed to read/parse auth file '{value}': {e}")
|
||||
|
||||
# Try parsing as inline JSON/YAML
|
||||
try:
|
||||
import json
|
||||
return json.loads(value)
|
||||
except ValueError:
|
||||
try:
|
||||
parsed = yaml.safe_load(value)
|
||||
if isinstance(parsed, dict):
|
||||
return parsed
|
||||
raise ValueError()
|
||||
except Exception:
|
||||
raise InvalidConfigurationError("Auth parameter must be a valid JSON/YAML string, or a path to a JSON/YAML file.")</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
<h3>Methods</h3>
|
||||
@@ -206,8 +256,10 @@ el.replaceWith(d);
|
||||
"theme": self.set_theme,
|
||||
"engineer_model": self.set_ai_config,
|
||||
"engineer_api_key": self.set_ai_config,
|
||||
"engineer_auth": self.set_ai_config,
|
||||
"architect_model": self.set_ai_config,
|
||||
"architect_api_key": self.set_ai_config,
|
||||
"architect_auth": self.set_ai_config,
|
||||
"trusted_commands": self.set_ai_config,
|
||||
"service_mode": self.set_service_mode,
|
||||
"remote_host": self.set_remote_host,
|
||||
@@ -234,10 +286,27 @@ el.replaceWith(d);
|
||||
try:
|
||||
settings = self.app.services.config_svc.get_settings()
|
||||
aiconfig = settings.get("ai", {})
|
||||
aiconfig[args.command] = args.data[0]
|
||||
val = args.data[0]
|
||||
|
||||
# Check for unset/clear request
|
||||
if val.lower() in ["none", "clear", ""]:
|
||||
if args.command in aiconfig:
|
||||
del aiconfig[args.command]
|
||||
else:
|
||||
# If configuring auth, parse as dictionary (JSON/YAML or file path)
|
||||
if args.command in ["engineer_auth", "architect_auth"]:
|
||||
parsed_val = self._parse_auth_value(val)
|
||||
if parsed_val is not None:
|
||||
aiconfig[args.command] = parsed_val
|
||||
else:
|
||||
if args.command in aiconfig:
|
||||
del aiconfig[args.command]
|
||||
else:
|
||||
aiconfig[args.command] = val
|
||||
|
||||
self.app.services.config_svc.update_setting("ai", aiconfig)
|
||||
printer.success("Config saved")
|
||||
except ConnpyError as e:
|
||||
except (ConnpyError, InvalidConfigurationError) as e:
|
||||
printer.error(str(e))</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
@@ -482,7 +551,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.cli.context_handler API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -249,7 +249,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.cli.forms API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -690,7 +690,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.cli.help_text API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -303,7 +303,7 @@ tasks:
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.cli.helpers API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -69,7 +69,7 @@ el.replaceWith(d);
|
||||
return answer[0]
|
||||
else:
|
||||
questions = [inquirer.List(name, message="Pick {} to {}:".format(name,action), choices=list_, carousel=True)]
|
||||
answer = inquirer.prompt(questions)
|
||||
answer = inquirer.prompt(questions, theme=theme)
|
||||
if answer == None:
|
||||
return None
|
||||
else:
|
||||
@@ -115,6 +115,65 @@ el.replaceWith(d);
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.helpers.get_theme"><code class="name flex">
|
||||
<span>def <span class="ident">get_theme</span></span>(<span>)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def get_theme():
|
||||
"""Returns a fresh instance of the theme with current colors."""
|
||||
return ConnpyTheme()</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Returns a fresh instance of the theme with current colors.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.helpers.hex_to_blessed"><code class="name flex">
|
||||
<span>def <span class="ident">hex_to_blessed</span></span>(<span>hex_str)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def hex_to_blessed(hex_str):
|
||||
"""Convert hex color string to blessed/ansi format."""
|
||||
if not hex_str or not isinstance(hex_str, str):
|
||||
return term.normal
|
||||
|
||||
# Check for bold prefix
|
||||
prefix = ""
|
||||
if hex_str.startswith('bold '):
|
||||
prefix = term.bold
|
||||
hex_str = hex_str.replace('bold ', '').strip()
|
||||
|
||||
# If it's a standard color name
|
||||
if not hex_str.startswith('#'):
|
||||
return prefix + getattr(term, hex_str, term.normal)
|
||||
|
||||
# Parse hex
|
||||
try:
|
||||
h = hex_str.lstrip('#')
|
||||
if len(h) == 3:
|
||||
h = ''.join([c*2 for c in h])
|
||||
r = int(h[0:2], 16)
|
||||
g = int(h[2:4], 16)
|
||||
b = int(h[4:6], 16)
|
||||
|
||||
# Try RGB, fallback to standard cyan if it fails or returns empty
|
||||
try:
|
||||
c = term.color_rgb(r, g, b)
|
||||
if not c: # Some terms return empty for RGB
|
||||
return prefix + term.cyan
|
||||
return prefix + c
|
||||
except:
|
||||
return prefix + term.cyan
|
||||
except:
|
||||
return prefix + term.normal</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Convert hex color string to blessed/ansi format.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.cli.helpers.nodes_completer"><code class="name flex">
|
||||
<span>def <span class="ident">nodes_completer</span></span>(<span>prefix, parsed_args, **kwargs)</span>
|
||||
</code></dt>
|
||||
@@ -181,6 +240,61 @@ el.replaceWith(d);
|
||||
</dl>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.cli.helpers.ConnpyTheme"><code class="flex name class">
|
||||
<span>class <span class="ident">ConnpyTheme</span></span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class ConnpyTheme(Default):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
try:
|
||||
from ..printer import _global_active_styles
|
||||
# Use user_prompt as primary accent, fallback to info/cyan
|
||||
accent = _global_active_styles.get("user_prompt", _global_active_styles.get("info", "cyan"))
|
||||
accent_color = hex_to_blessed(accent)
|
||||
|
||||
self.Question.mark_color = accent_color
|
||||
self.List.selection_color = accent_color
|
||||
self.List.selection_cursor = ">"
|
||||
except:
|
||||
# Absolute fallback to standard cyan
|
||||
self.Question.mark_color = term.cyan
|
||||
self.List.selection_color = term.bold_cyan
|
||||
self.List.selection_cursor = ">"</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>inquirer.themes.Default</li>
|
||||
<li>inquirer.themes.Theme</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt id="connpy.cli.helpers.ThemeProxy"><code class="flex name class">
|
||||
<span>class <span class="ident">ThemeProxy</span></span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">class ThemeProxy:
|
||||
"""Proxy to ensure theme colors are resolved at runtime."""
|
||||
def __getattr__(self, name):
|
||||
return getattr(get_theme(), name)
|
||||
def __iter__(self):
|
||||
return iter(get_theme())
|
||||
def __getitem__(self, item):
|
||||
return get_theme()[item]</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Proxy to ensure theme colors are resolved at runtime.</p></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</section>
|
||||
</article>
|
||||
<nav id="sidebar">
|
||||
@@ -198,16 +312,28 @@ el.replaceWith(d);
|
||||
<li><code><a title="connpy.cli.helpers.choose" href="#connpy.cli.helpers.choose">choose</a></code></li>
|
||||
<li><code><a title="connpy.cli.helpers.folders_completer" href="#connpy.cli.helpers.folders_completer">folders_completer</a></code></li>
|
||||
<li><code><a title="connpy.cli.helpers.get_config_dir" href="#connpy.cli.helpers.get_config_dir">get_config_dir</a></code></li>
|
||||
<li><code><a title="connpy.cli.helpers.get_theme" href="#connpy.cli.helpers.get_theme">get_theme</a></code></li>
|
||||
<li><code><a title="connpy.cli.helpers.hex_to_blessed" href="#connpy.cli.helpers.hex_to_blessed">hex_to_blessed</a></code></li>
|
||||
<li><code><a title="connpy.cli.helpers.nodes_completer" href="#connpy.cli.helpers.nodes_completer">nodes_completer</a></code></li>
|
||||
<li><code><a title="connpy.cli.helpers.profiles_completer" href="#connpy.cli.helpers.profiles_completer">profiles_completer</a></code></li>
|
||||
<li><code><a title="connpy.cli.helpers.toplevel_completer" href="#connpy.cli.helpers.toplevel_completer">toplevel_completer</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.cli.helpers.ConnpyTheme" href="#connpy.cli.helpers.ConnpyTheme">ConnpyTheme</a></code></h4>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.cli.helpers.ThemeProxy" href="#connpy.cli.helpers.ThemeProxy">ThemeProxy</a></code></h4>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.cli.import_export_handler API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -272,7 +272,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.cli API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -142,7 +142,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.cli.node_handler API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -606,7 +606,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.cli.plugin_handler API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -385,7 +385,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.cli.profile_handler API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -314,7 +314,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.cli.run_handler API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -454,7 +454,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.cli.sync_handler API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -427,7 +427,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.cli.terminal_ui API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -168,7 +168,8 @@ el.replaceWith(d);
|
||||
if state['context_mode'] == self.mode_single:
|
||||
active_raw = raw_bytes[start:end]
|
||||
else:
|
||||
active_raw = raw_bytes[start:]
|
||||
# Concat only the bytes of valid blocks to skip intermediate empty/cancelled prompt noise
|
||||
active_raw = b"".join(raw_bytes[b[0]:b[1]] for b in blocks[idx:])
|
||||
return preview + "\n" + log_cleaner(active_raw.decode(errors='replace'))
|
||||
|
||||
def get_prompt_text():
|
||||
@@ -207,7 +208,6 @@ el.replaceWith(d);
|
||||
base_str = f'\u25b6 Ctrl+\u2191/\u2193 adjusts by 50 lines [Tab: {m_label}]'
|
||||
else:
|
||||
idx = max(0, state['total_cmds'] - state['context_cmd'])
|
||||
import re
|
||||
|
||||
def clean_preview(text):
|
||||
# Limpia saltos de línea y el prompt inicial (todo hasta #, > o $) para que quede solo el comando
|
||||
@@ -370,10 +370,10 @@ el.replaceWith(d);
|
||||
persona_title = "Network Architect" if active_persona == "architect" else "Network Engineer"
|
||||
|
||||
active_buffer = get_active_buffer()
|
||||
|
||||
live_text = ""
|
||||
first_chunk = True
|
||||
|
||||
import sys
|
||||
from rich.rule import Rule
|
||||
from rich.status import Status
|
||||
from connpy.printer import IncrementalMarkdownParser
|
||||
@@ -622,7 +622,8 @@ el.replaceWith(d);
|
||||
if state['context_mode'] == self.mode_single:
|
||||
active_raw = raw_bytes[start:end]
|
||||
else:
|
||||
active_raw = raw_bytes[start:]
|
||||
# Concat only the bytes of valid blocks to skip intermediate empty/cancelled prompt noise
|
||||
active_raw = b"".join(raw_bytes[b[0]:b[1]] for b in blocks[idx:])
|
||||
return preview + "\n" + log_cleaner(active_raw.decode(errors='replace'))
|
||||
|
||||
def get_prompt_text():
|
||||
@@ -661,7 +662,6 @@ el.replaceWith(d);
|
||||
base_str = f'\u25b6 Ctrl+\u2191/\u2193 adjusts by 50 lines [Tab: {m_label}]'
|
||||
else:
|
||||
idx = max(0, state['total_cmds'] - state['context_cmd'])
|
||||
import re
|
||||
|
||||
def clean_preview(text):
|
||||
# Limpia saltos de línea y el prompt inicial (todo hasta #, > o $) para que quede solo el comando
|
||||
@@ -824,10 +824,10 @@ el.replaceWith(d);
|
||||
persona_title = "Network Architect" if active_persona == "architect" else "Network Engineer"
|
||||
|
||||
active_buffer = get_active_buffer()
|
||||
|
||||
live_text = ""
|
||||
first_chunk = True
|
||||
|
||||
import sys
|
||||
from rich.rule import Rule
|
||||
from rich.status import Status
|
||||
from connpy.printer import IncrementalMarkdownParser
|
||||
@@ -1017,7 +1017,7 @@ on_ai_call: async function(active_buffer, question) -> result_dict</p></div>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.cli.validators API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -508,7 +508,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.grpc_layer.connpy_pb2 API documentation</title>
|
||||
<meta name="description" content="Generated protocol buffer code.">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -45,617 +45,6 @@ el.replaceWith(d);
|
||||
<section>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.AIResponse"><code class="flex name class">
|
||||
<span>class <span class="ident">AIResponse</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.AIResponse.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.AskRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">AskRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.AskRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.BoolResponse"><code class="flex name class">
|
||||
<span>class <span class="ident">BoolResponse</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.BoolResponse.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.BulkRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">BulkRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.BulkRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.CopilotRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">CopilotRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.CopilotRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.CopilotResponse"><code class="flex name class">
|
||||
<span>class <span class="ident">CopilotResponse</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.CopilotResponse.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.DeleteRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">DeleteRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.DeleteRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.ExportRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">ExportRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.ExportRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.FilterRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">FilterRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.FilterRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.FullReplaceRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">FullReplaceRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.FullReplaceRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.IdRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">IdRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.IdRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.IntRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">IntRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.IntRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.InteractRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">InteractRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.InteractRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.InteractResponse"><code class="flex name class">
|
||||
<span>class <span class="ident">InteractResponse</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.InteractResponse.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.ListRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">ListRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.ListRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.MCPRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">MCPRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.MCPRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.MessageValue"><code class="flex name class">
|
||||
<span>class <span class="ident">MessageValue</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.MessageValue.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.MoveRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">MoveRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.MoveRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.NodeRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">NodeRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.NodeRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.NodeRunResult"><code class="flex name class">
|
||||
<span>class <span class="ident">NodeRunResult</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.NodeRunResult.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.PluginRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">PluginRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.PluginRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.ProfileRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">ProfileRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.ProfileRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.ProviderRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">ProviderRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.ProviderRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.RunRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">RunRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.RunRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.ScriptRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">ScriptRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.ScriptRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.StringRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">StringRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.StringRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.StringResponse"><code class="flex name class">
|
||||
<span>class <span class="ident">StringResponse</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.StringResponse.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.StructRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">StructRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.StructRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.StructResponse"><code class="flex name class">
|
||||
<span>class <span class="ident">StructResponse</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.StructResponse.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.TestRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">TestRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.TestRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.UpdateRequest"><code class="flex name class">
|
||||
<span>class <span class="ident">UpdateRequest</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.UpdateRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.ValueResponse"><code class="flex name class">
|
||||
<span>class <span class="ident">ValueResponse</span></span>
|
||||
<span>(</span><span>*args, **kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>A ProtocolMessage</p></div>
|
||||
<h3>Ancestors</h3>
|
||||
<ul class="hlist">
|
||||
<li>google._upb._message.Message</li>
|
||||
<li>google.protobuf.message.Message</li>
|
||||
</ul>
|
||||
<h3>Class variables</h3>
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.connpy_pb2.ValueResponse.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
</section>
|
||||
</article>
|
||||
<nav id="sidebar">
|
||||
@@ -668,207 +57,11 @@ el.replaceWith(d);
|
||||
<li><code><a title="connpy.grpc_layer" href="index.html">connpy.grpc_layer</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.AIResponse" href="#connpy.grpc_layer.connpy_pb2.AIResponse">AIResponse</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.AIResponse.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.AIResponse.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.AskRequest" href="#connpy.grpc_layer.connpy_pb2.AskRequest">AskRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.AskRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.AskRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.BoolResponse" href="#connpy.grpc_layer.connpy_pb2.BoolResponse">BoolResponse</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.BoolResponse.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.BoolResponse.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.BulkRequest" href="#connpy.grpc_layer.connpy_pb2.BulkRequest">BulkRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.BulkRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.BulkRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.CopilotRequest" href="#connpy.grpc_layer.connpy_pb2.CopilotRequest">CopilotRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.CopilotRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.CopilotRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.CopilotResponse" href="#connpy.grpc_layer.connpy_pb2.CopilotResponse">CopilotResponse</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.CopilotResponse.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.CopilotResponse.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.DeleteRequest" href="#connpy.grpc_layer.connpy_pb2.DeleteRequest">DeleteRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.DeleteRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.DeleteRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.ExportRequest" href="#connpy.grpc_layer.connpy_pb2.ExportRequest">ExportRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.ExportRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.ExportRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.FilterRequest" href="#connpy.grpc_layer.connpy_pb2.FilterRequest">FilterRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.FilterRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.FilterRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.FullReplaceRequest" href="#connpy.grpc_layer.connpy_pb2.FullReplaceRequest">FullReplaceRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.FullReplaceRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.FullReplaceRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.IdRequest" href="#connpy.grpc_layer.connpy_pb2.IdRequest">IdRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.IdRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.IdRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.IntRequest" href="#connpy.grpc_layer.connpy_pb2.IntRequest">IntRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.IntRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.IntRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.InteractRequest" href="#connpy.grpc_layer.connpy_pb2.InteractRequest">InteractRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.InteractRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.InteractRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.InteractResponse" href="#connpy.grpc_layer.connpy_pb2.InteractResponse">InteractResponse</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.InteractResponse.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.InteractResponse.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.ListRequest" href="#connpy.grpc_layer.connpy_pb2.ListRequest">ListRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.ListRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.ListRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.MCPRequest" href="#connpy.grpc_layer.connpy_pb2.MCPRequest">MCPRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.MCPRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.MCPRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.MessageValue" href="#connpy.grpc_layer.connpy_pb2.MessageValue">MessageValue</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.MessageValue.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.MessageValue.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.MoveRequest" href="#connpy.grpc_layer.connpy_pb2.MoveRequest">MoveRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.MoveRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.MoveRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.NodeRequest" href="#connpy.grpc_layer.connpy_pb2.NodeRequest">NodeRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.NodeRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.NodeRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.NodeRunResult" href="#connpy.grpc_layer.connpy_pb2.NodeRunResult">NodeRunResult</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.NodeRunResult.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.NodeRunResult.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.PluginRequest" href="#connpy.grpc_layer.connpy_pb2.PluginRequest">PluginRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.PluginRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.PluginRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.ProfileRequest" href="#connpy.grpc_layer.connpy_pb2.ProfileRequest">ProfileRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.ProfileRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.ProfileRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.ProviderRequest" href="#connpy.grpc_layer.connpy_pb2.ProviderRequest">ProviderRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.ProviderRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.ProviderRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.RunRequest" href="#connpy.grpc_layer.connpy_pb2.RunRequest">RunRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.RunRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.RunRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.ScriptRequest" href="#connpy.grpc_layer.connpy_pb2.ScriptRequest">ScriptRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.ScriptRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.ScriptRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.StringRequest" href="#connpy.grpc_layer.connpy_pb2.StringRequest">StringRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.StringRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.StringRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.StringResponse" href="#connpy.grpc_layer.connpy_pb2.StringResponse">StringResponse</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.StringResponse.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.StringResponse.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.StructRequest" href="#connpy.grpc_layer.connpy_pb2.StructRequest">StructRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.StructRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.StructRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.StructResponse" href="#connpy.grpc_layer.connpy_pb2.StructResponse">StructResponse</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.StructResponse.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.StructResponse.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.TestRequest" href="#connpy.grpc_layer.connpy_pb2.TestRequest">TestRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.TestRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.TestRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.UpdateRequest" href="#connpy.grpc_layer.connpy_pb2.UpdateRequest">UpdateRequest</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.UpdateRequest.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.UpdateRequest.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4><code><a title="connpy.grpc_layer.connpy_pb2.ValueResponse" href="#connpy.grpc_layer.connpy_pb2.ValueResponse">ValueResponse</a></code></h4>
|
||||
<ul class="">
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2.ValueResponse.DESCRIPTOR" href="#connpy.grpc_layer.connpy_pb2.ValueResponse.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.grpc_layer API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -102,7 +102,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.grpc_layer.remote_plugin_pb2 API documentation</title>
|
||||
<meta name="description" content="Generated protocol buffer code.">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -62,7 +62,7 @@ el.replaceWith(d);
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.remote_plugin_pb2.IdRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
<div class="desc"><p>The type of the None singleton.</p></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
@@ -81,7 +81,7 @@ el.replaceWith(d);
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.remote_plugin_pb2.OutputChunk.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
<div class="desc"><p>The type of the None singleton.</p></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
@@ -100,7 +100,7 @@ el.replaceWith(d);
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.remote_plugin_pb2.PluginInvokeRequest.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
<div class="desc"><p>The type of the None singleton.</p></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
@@ -119,7 +119,7 @@ el.replaceWith(d);
|
||||
<dl>
|
||||
<dt id="connpy.grpc_layer.remote_plugin_pb2.StringResponse.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
<div class="desc"><p>The type of the None singleton.</p></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
@@ -168,7 +168,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.grpc_layer.remote_plugin_pb2_grpc API documentation</title>
|
||||
<meta name="description" content="Client and server classes corresponding to protobuf-defined services.">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -366,7 +366,7 @@ def invoke_plugin(request,
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.grpc_layer.server API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -177,9 +177,11 @@ el.replaceWith(d);
|
||||
print(f"AI Task Error: {e}")
|
||||
traceback.print_exc()
|
||||
chunk_queue.put(("status", f"Error: {str(e)}"))
|
||||
# Crucial: always send final_mark to avoid client deadlock
|
||||
chunk_queue.put(("final_mark", {"response": f"Error: {str(e)}", "chat_history": history, "error": True}))
|
||||
|
||||
def request_listener():
|
||||
nonlocal bridge, is_web, ai_thread, agent_instance
|
||||
nonlocal bridge, is_web, ai_thread, agent_instance, history
|
||||
try:
|
||||
for req in request_iterator:
|
||||
if req.interrupt:
|
||||
@@ -193,12 +195,21 @@ el.replaceWith(d);
|
||||
|
||||
if req.input_text:
|
||||
is_web = "web" in (req.session_id or "").lower() or (req.session_id or "").lower().startswith("ws-")
|
||||
|
||||
# Hydrate history from client if it's the first interaction in this stream
|
||||
if not history and req.chat_history:
|
||||
from .utils import from_value
|
||||
history = from_value(req.chat_history) or []
|
||||
if not bridge:
|
||||
bridge = StatusBridge(chunk_queue, request_queue=request_queue, is_web=is_web)
|
||||
|
||||
overrides = {}
|
||||
if req.engineer_model: overrides["engineer_model"] = req.engineer_model
|
||||
if req.engineer_api_key: overrides["engineer_api_key"] = req.engineer_api_key
|
||||
if req.architect_model: overrides["architect_model"] = req.architect_model
|
||||
if req.architect_api_key: overrides["architect_api_key"] = req.architect_api_key
|
||||
if req.HasField("engineer_auth"): overrides["engineer_auth"] = from_struct(req.engineer_auth)
|
||||
if req.HasField("architect_auth"): overrides["architect_auth"] = from_struct(req.architect_auth)
|
||||
|
||||
# Start AI in its own thread so we can keep listening for interrupts
|
||||
ai_thread = threading.Thread(
|
||||
@@ -263,7 +274,8 @@ el.replaceWith(d);
|
||||
|
||||
@handle_errors
|
||||
def list_sessions(self, request, context):
|
||||
return connpy_pb2.ValueResponse(data=to_value(self.service.list_sessions()))
|
||||
sessions, total = self.service.list_sessions()
|
||||
return connpy_pb2.ValueResponse(data=to_value(sessions))
|
||||
|
||||
@handle_errors
|
||||
def delete_session(self, request, context):
|
||||
@@ -272,7 +284,8 @@ el.replaceWith(d);
|
||||
|
||||
@handle_errors
|
||||
def configure_provider(self, request, context):
|
||||
self.service.configure_provider(request.provider, request.model, request.api_key)
|
||||
auth_dict = from_struct(request.auth) if request.HasField("auth") else None
|
||||
self.service.configure_provider(request.provider, request.model, request.api_key, auth=auth_dict)
|
||||
return Empty()
|
||||
|
||||
@handle_errors
|
||||
@@ -286,6 +299,11 @@ el.replaceWith(d);
|
||||
)
|
||||
return Empty()
|
||||
|
||||
@handle_errors
|
||||
def list_mcp_servers(self, request, context):
|
||||
mcp_servers = self.service.list_mcp_servers()
|
||||
return connpy_pb2.ValueResponse(data=to_value(mcp_servers))
|
||||
|
||||
@handle_errors
|
||||
def load_session_data(self, request, context):
|
||||
return connpy_pb2.StructResponse(data=to_struct(self.service.load_session_data(request.value)))</code></pre>
|
||||
@@ -305,6 +323,7 @@ el.replaceWith(d);
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2_grpc.AIServiceServicer.configure_provider" href="connpy_pb2_grpc.html#connpy.grpc_layer.connpy_pb2_grpc.AIServiceServicer.configure_provider">configure_provider</a></code></li>
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2_grpc.AIServiceServicer.confirm" href="connpy_pb2_grpc.html#connpy.grpc_layer.connpy_pb2_grpc.AIServiceServicer.confirm">confirm</a></code></li>
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2_grpc.AIServiceServicer.delete_session" href="connpy_pb2_grpc.html#connpy.grpc_layer.connpy_pb2_grpc.AIServiceServicer.delete_session">delete_session</a></code></li>
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2_grpc.AIServiceServicer.list_mcp_servers" href="connpy_pb2_grpc.html#connpy.grpc_layer.connpy_pb2_grpc.AIServiceServicer.list_mcp_servers">list_mcp_servers</a></code></li>
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2_grpc.AIServiceServicer.list_sessions" href="connpy_pb2_grpc.html#connpy.grpc_layer.connpy_pb2_grpc.AIServiceServicer.list_sessions">list_sessions</a></code></li>
|
||||
<li><code><a title="connpy.grpc_layer.connpy_pb2_grpc.AIServiceServicer.load_session_data" href="connpy_pb2_grpc.html#connpy.grpc_layer.connpy_pb2_grpc.AIServiceServicer.load_session_data">load_session_data</a></code></li>
|
||||
</ul>
|
||||
@@ -975,7 +994,9 @@ interceptor chooses to service this RPC, or None otherwise.</p></div>
|
||||
|
||||
asyncio.run(n._async_interact_loop(remote_stream, resize_callback, copilot_handler=remote_copilot_handler))
|
||||
except Exception as e:
|
||||
pass
|
||||
import traceback
|
||||
print(f"[ERROR in run_async_loop] {e}")
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
n._teardown_interact_environment()
|
||||
response_queue.put(None) # Signal EOF
|
||||
@@ -1195,6 +1216,7 @@ interceptor chooses to service this RPC, or None otherwise.</p></div>
|
||||
self.service = ProfileService(config)
|
||||
self.node_service = NodeService(config)
|
||||
|
||||
|
||||
@handle_errors
|
||||
def list_profiles(self, request, context):
|
||||
f = request.filter_str if request.filter_str else None
|
||||
@@ -1261,6 +1283,7 @@ interceptor chooses to service this RPC, or None otherwise.</p></div>
|
||||
self.on_interrupt = self._force_interrupt
|
||||
self.thread = None
|
||||
self.is_web = is_web
|
||||
self.is_remote = True
|
||||
|
||||
def _force_interrupt(self):
|
||||
"""Forcefully raise KeyboardInterrupt in the target thread."""
|
||||
@@ -1560,7 +1583,7 @@ interceptor chooses to service this RPC, or None otherwise.</p></div>
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.grpc_layer.stubs API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -122,6 +122,10 @@ el.replaceWith(d);
|
||||
)
|
||||
if chat_history is not None:
|
||||
initial_req.chat_history.CopyFrom(to_value(chat_history))
|
||||
if "engineer_auth" in overrides and overrides["engineer_auth"]:
|
||||
initial_req.engineer_auth.CopyFrom(to_struct(overrides["engineer_auth"]))
|
||||
if "architect_auth" in overrides and overrides["architect_auth"]:
|
||||
initial_req.architect_auth.CopyFrom(to_struct(overrides["architect_auth"]))
|
||||
|
||||
req_queue.put(initial_req)
|
||||
|
||||
@@ -135,6 +139,7 @@ el.replaceWith(d);
|
||||
|
||||
full_content = ""
|
||||
header_printed = False
|
||||
current_responder = "engineer"
|
||||
final_result = {"response": "", "chat_history": []}
|
||||
|
||||
# Background thread to pull responses from gRPC into a local queue
|
||||
@@ -179,6 +184,10 @@ el.replaceWith(d);
|
||||
break
|
||||
|
||||
if response.status_update:
|
||||
if response.status_update.startswith("__RESPONDER__:"):
|
||||
current_responder = response.status_update.split(":")[1].lower()
|
||||
continue
|
||||
|
||||
if response.requires_confirmation:
|
||||
if status: status.stop()
|
||||
|
||||
@@ -231,7 +240,9 @@ el.replaceWith(d);
|
||||
stable_console = RichConsole(theme=connpy_theme, file=get_original_stdout())
|
||||
|
||||
# Print header on first chunk
|
||||
stable_console.print(Rule("[bold engineer]Network Engineer[/bold engineer]", style="engineer"))
|
||||
alias = "architect" if current_responder == "architect" else "engineer"
|
||||
role_label = "Network Architect" if current_responder == "architect" else "Network Engineer"
|
||||
stable_console.print(Rule(f"[bold {alias}]{role_label}[/bold {alias}]", style=alias))
|
||||
header_printed = True
|
||||
|
||||
# Initialize parser
|
||||
@@ -283,16 +294,23 @@ el.replaceWith(d);
|
||||
return self.stub.confirm(connpy_pb2.StringRequest(value=input_text)).value
|
||||
|
||||
@handle_errors
|
||||
def list_sessions(self):
|
||||
return from_value(self.stub.list_sessions(Empty()).data)
|
||||
def list_sessions(self, limit=None):
|
||||
from .utils import from_value
|
||||
res = self.stub.list_sessions(Empty())
|
||||
sessions = from_value(res.data) or []
|
||||
if limit and len(sessions) > limit:
|
||||
return sessions[:limit], len(sessions)
|
||||
return sessions, len(sessions)
|
||||
|
||||
@handle_errors
|
||||
def delete_session(self, session_id):
|
||||
self.stub.delete_session(connpy_pb2.StringRequest(value=session_id))
|
||||
|
||||
@handle_errors
|
||||
def configure_provider(self, provider, model=None, api_key=None):
|
||||
def configure_provider(self, provider, model=None, api_key=None, auth=None):
|
||||
req = connpy_pb2.ProviderRequest(provider=provider, model=model or "", api_key=api_key or "")
|
||||
if auth:
|
||||
req.auth.CopyFrom(to_struct(auth))
|
||||
self.stub.configure_provider(req)
|
||||
|
||||
@handle_errors
|
||||
@@ -306,6 +324,11 @@ el.replaceWith(d);
|
||||
)
|
||||
self.stub.configure_mcp(req)
|
||||
|
||||
@handle_errors
|
||||
def list_mcp_servers(self):
|
||||
res = self.stub.list_mcp_servers(Empty())
|
||||
return from_value(res.data) or {}
|
||||
|
||||
@handle_errors
|
||||
def load_session_data(self, session_id):
|
||||
return from_struct(self.stub.load_session_data(connpy_pb2.StringRequest(value=session_id)).data)</code></pre>
|
||||
@@ -344,6 +367,10 @@ def ask(self, input_text, dryrun=False, chat_history=None, session_id=None, debu
|
||||
)
|
||||
if chat_history is not None:
|
||||
initial_req.chat_history.CopyFrom(to_value(chat_history))
|
||||
if "engineer_auth" in overrides and overrides["engineer_auth"]:
|
||||
initial_req.engineer_auth.CopyFrom(to_struct(overrides["engineer_auth"]))
|
||||
if "architect_auth" in overrides and overrides["architect_auth"]:
|
||||
initial_req.architect_auth.CopyFrom(to_struct(overrides["architect_auth"]))
|
||||
|
||||
req_queue.put(initial_req)
|
||||
|
||||
@@ -357,6 +384,7 @@ def ask(self, input_text, dryrun=False, chat_history=None, session_id=None, debu
|
||||
|
||||
full_content = ""
|
||||
header_printed = False
|
||||
current_responder = "engineer"
|
||||
final_result = {"response": "", "chat_history": []}
|
||||
|
||||
# Background thread to pull responses from gRPC into a local queue
|
||||
@@ -401,6 +429,10 @@ def ask(self, input_text, dryrun=False, chat_history=None, session_id=None, debu
|
||||
break
|
||||
|
||||
if response.status_update:
|
||||
if response.status_update.startswith("__RESPONDER__:"):
|
||||
current_responder = response.status_update.split(":")[1].lower()
|
||||
continue
|
||||
|
||||
if response.requires_confirmation:
|
||||
if status: status.stop()
|
||||
|
||||
@@ -453,7 +485,9 @@ def ask(self, input_text, dryrun=False, chat_history=None, session_id=None, debu
|
||||
stable_console = RichConsole(theme=connpy_theme, file=get_original_stdout())
|
||||
|
||||
# Print header on first chunk
|
||||
stable_console.print(Rule("[bold engineer]Network Engineer[/bold engineer]", style="engineer"))
|
||||
alias = "architect" if current_responder == "architect" else "engineer"
|
||||
role_label = "Network Architect" if current_responder == "architect" else "Network Engineer"
|
||||
stable_console.print(Rule(f"[bold {alias}]{role_label}[/bold {alias}]", style=alias))
|
||||
header_printed = True
|
||||
|
||||
# Initialize parser
|
||||
@@ -524,7 +558,7 @@ def configure_mcp(self, name, url=None, enabled=True, auto_load_on_os=None, remo
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.stubs.AIStub.configure_provider"><code class="name flex">
|
||||
<span>def <span class="ident">configure_provider</span></span>(<span>self, provider, model=None, api_key=None)</span>
|
||||
<span>def <span class="ident">configure_provider</span></span>(<span>self, provider, model=None, api_key=None, auth=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
@@ -532,8 +566,10 @@ def configure_mcp(self, name, url=None, enabled=True, auto_load_on_os=None, remo
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@handle_errors
|
||||
def configure_provider(self, provider, model=None, api_key=None):
|
||||
def configure_provider(self, provider, model=None, api_key=None, auth=None):
|
||||
req = connpy_pb2.ProviderRequest(provider=provider, model=model or "", api_key=api_key or "")
|
||||
if auth:
|
||||
req.auth.CopyFrom(to_struct(auth))
|
||||
self.stub.configure_provider(req)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
@@ -566,8 +602,8 @@ def delete_session(self, session_id):
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.stubs.AIStub.list_sessions"><code class="name flex">
|
||||
<span>def <span class="ident">list_sessions</span></span>(<span>self)</span>
|
||||
<dt id="connpy.grpc_layer.stubs.AIStub.list_mcp_servers"><code class="name flex">
|
||||
<span>def <span class="ident">list_mcp_servers</span></span>(<span>self)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
@@ -575,8 +611,28 @@ def delete_session(self, session_id):
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@handle_errors
|
||||
def list_sessions(self):
|
||||
return from_value(self.stub.list_sessions(Empty()).data)</code></pre>
|
||||
def list_mcp_servers(self):
|
||||
res = self.stub.list_mcp_servers(Empty())
|
||||
return from_value(res.data) or {}</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt id="connpy.grpc_layer.stubs.AIStub.list_sessions"><code class="name flex">
|
||||
<span>def <span class="ident">list_sessions</span></span>(<span>self, limit=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@handle_errors
|
||||
def list_sessions(self, limit=None):
|
||||
from .utils import from_value
|
||||
res = self.stub.list_sessions(Empty())
|
||||
sessions = from_value(res.data) or []
|
||||
if limit and len(sessions) > limit:
|
||||
return sessions[:limit], len(sessions)
|
||||
return sessions, len(sessions)</code></pre>
|
||||
</details>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
@@ -2470,6 +2526,7 @@ def stop_api(self):
|
||||
<li><code><a title="connpy.grpc_layer.stubs.AIStub.configure_provider" href="#connpy.grpc_layer.stubs.AIStub.configure_provider">configure_provider</a></code></li>
|
||||
<li><code><a title="connpy.grpc_layer.stubs.AIStub.confirm" href="#connpy.grpc_layer.stubs.AIStub.confirm">confirm</a></code></li>
|
||||
<li><code><a title="connpy.grpc_layer.stubs.AIStub.delete_session" href="#connpy.grpc_layer.stubs.AIStub.delete_session">delete_session</a></code></li>
|
||||
<li><code><a title="connpy.grpc_layer.stubs.AIStub.list_mcp_servers" href="#connpy.grpc_layer.stubs.AIStub.list_mcp_servers">list_mcp_servers</a></code></li>
|
||||
<li><code><a title="connpy.grpc_layer.stubs.AIStub.list_sessions" href="#connpy.grpc_layer.stubs.AIStub.list_sessions">list_sessions</a></code></li>
|
||||
<li><code><a title="connpy.grpc_layer.stubs.AIStub.load_session_data" href="#connpy.grpc_layer.stubs.AIStub.load_session_data">load_session_data</a></code></li>
|
||||
</ul>
|
||||
@@ -2561,7 +2618,7 @@ def stop_api(self):
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.grpc_layer.utils API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -138,7 +138,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+157
-62
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy API documentation</title>
|
||||
<meta name="description" content="<p align="center">
|
||||
<img src="https://nginx.gederico.dynu.net/images/CONNPY-resized.png" alt="App Logo">
|
||||
@@ -205,10 +205,6 @@ response = myai.ask("What is the status of the BGP neighbors in the office?
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.tests" href="tests/index.html">connpy.tests</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
</dd>
|
||||
<dt><code class="name"><a title="connpy.tunnels" href="tunnels.html">connpy.tunnels</a></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
@@ -620,7 +616,7 @@ indicating successful verification.</p>
|
||||
</dd>
|
||||
<dt id="connpy.ai"><code class="flex name class">
|
||||
<span>class <span class="ident">ai</span></span>
|
||||
<span>(</span><span>config,<br>org=None,<br>api_key=None,<br>engineer_model=None,<br>architect_model=None,<br>engineer_api_key=None,<br>architect_api_key=None,<br>console=None,<br>confirm_handler=None,<br>trust=False)</span>
|
||||
<span>(</span><span>config,<br>org=None,<br>api_key=None,<br>engineer_model=None,<br>architect_model=None,<br>engineer_api_key=None,<br>architect_api_key=None,<br>console=None,<br>confirm_handler=None,<br>trust=False,<br>engineer_auth=None,<br>architect_auth=None,<br>**kwargs)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
@@ -638,7 +634,7 @@ class ai:
|
||||
r'^systemctl\s+status\s+', r'^journalctl\s+'
|
||||
]
|
||||
|
||||
def __init__(self, config, org=None, api_key=None, engineer_model=None, architect_model=None, engineer_api_key=None, architect_api_key=None, console=None, confirm_handler=None, trust=False):
|
||||
def __init__(self, config, org=None, api_key=None, engineer_model=None, architect_model=None, engineer_api_key=None, architect_api_key=None, console=None, confirm_handler=None, trust=False, engineer_auth=None, architect_auth=None, **kwargs):
|
||||
self.config = config
|
||||
self.console = console or printer.console
|
||||
self.confirm_handler = confirm_handler or self._local_confirm_handler
|
||||
@@ -657,6 +653,29 @@ class ai:
|
||||
self.engineer_key = engineer_api_key or aiconfig.get("engineer_api_key")
|
||||
self.architect_key = architect_api_key or aiconfig.get("architect_api_key")
|
||||
|
||||
# Auth configurations (Prioridad: Argumento -> Config)
|
||||
self.engineer_auth = engineer_auth if engineer_auth is not None else aiconfig.get("engineer_auth")
|
||||
if self.engineer_auth is None:
|
||||
self.engineer_auth = {}
|
||||
elif not isinstance(self.engineer_auth, dict):
|
||||
self.engineer_auth = {}
|
||||
|
||||
self.architect_auth = architect_auth if architect_auth is not None else aiconfig.get("architect_auth")
|
||||
if self.architect_auth is None:
|
||||
self.architect_auth = {}
|
||||
elif not isinstance(self.architect_auth, dict):
|
||||
self.architect_auth = {}
|
||||
|
||||
# Backward compatibility fallbacks: only inject api_key if the auth dict is empty/not configured
|
||||
if self.engineer_key and not self.engineer_auth:
|
||||
self.engineer_auth["api_key"] = self.engineer_key
|
||||
if self.architect_key and not self.architect_auth:
|
||||
self.architect_auth["api_key"] = self.architect_key
|
||||
|
||||
# Strategic Reasoning Engine (Architect) availability
|
||||
is_architect_keyless = "vertex" in self.architect_model.lower() or "ollama" in self.architect_model.lower() or "local" in self.architect_model.lower()
|
||||
self.has_architect = bool(self.architect_key or self.architect_auth or is_architect_keyless)
|
||||
|
||||
# Custom Trusted Commands Regexes
|
||||
custom_trusted = aiconfig.get("trusted_commands", [])
|
||||
if isinstance(custom_trusted, str):
|
||||
@@ -697,12 +716,12 @@ class ai:
|
||||
# Session Management
|
||||
self.sessions_dir = os.path.join(self.config.defaultdir, "ai_sessions")
|
||||
os.makedirs(self.sessions_dir, exist_ok=True)
|
||||
self.session_id = None
|
||||
self.session_path = None
|
||||
self.session_id = getattr(self.config, "session_id", None)
|
||||
self.session_path = os.path.join(self.sessions_dir, f"{self.session_id}.json") if self.session_id else None
|
||||
|
||||
# Prompts base agnósticos
|
||||
architect_instructions = ""
|
||||
if self.architect_key:
|
||||
if self.has_architect:
|
||||
architect_instructions = """
|
||||
CRITICAL - CONSULT vs ESCALATE:
|
||||
- ALWAYS use 'consult_architect' for: Configuration planning, design decisions, complex troubleshooting.
|
||||
@@ -718,7 +737,7 @@ class ai:
|
||||
else:
|
||||
architect_instructions = """
|
||||
CRITICAL - ARCHITECT UNAVAILABLE:
|
||||
- The Strategic Reasoning Engine (Architect) is currently UNAVAILABLE because its API key is not configured.
|
||||
- The Strategic Reasoning Engine (Architect) is currently UNAVAILABLE because its API key or authentication is not configured.
|
||||
- DO NOT attempt to consult or escalate to the architect.
|
||||
- If the user asks to consult the architect, inform them that the Architect is offline and offer to help them directly to the best of your abilities.
|
||||
"""
|
||||
@@ -824,15 +843,19 @@ class ai:
|
||||
if status_formatter:
|
||||
self.tool_status_formatters[name] = status_formatter
|
||||
|
||||
def _stream_completion(self, model, messages, tools, api_key, status=None, label="", debug=False, chunk_callback=None, **kwargs):
|
||||
def _stream_completion(self, model, messages, tools, api_key=None, status=None, label="", debug=False, chunk_callback=None, auth=None, **kwargs):
|
||||
"""Stream a completion call, rendering styled Markdown in real-time.
|
||||
|
||||
Returns (response, streamed) where:
|
||||
- response: reconstructed ModelResponse (same as non-streaming)
|
||||
- streamed: True if text was rendered to console during streaming
|
||||
"""
|
||||
auth_dict = auth if auth is not None else {}
|
||||
if api_key and "api_key" not in auth_dict:
|
||||
auth_dict = auth_dict.copy()
|
||||
auth_dict["api_key"] = api_key
|
||||
|
||||
stream_resp = completion(model=model, messages=messages, tools=tools, api_key=api_key, stream=True, **kwargs)
|
||||
stream_resp = completion(model=model, messages=messages, tools=tools, stream=True, **auth_dict, **kwargs)
|
||||
|
||||
chunks = []
|
||||
full_content = ""
|
||||
@@ -1275,7 +1298,7 @@ class ai:
|
||||
|
||||
try:
|
||||
safe_messages = self._sanitize_messages(messages)
|
||||
response = completion(model=self.engineer_model, messages=safe_messages, tools=tools, api_key=self.engineer_key)
|
||||
response = completion(model=self.engineer_model, messages=safe_messages, tools=tools, **self.engineer_auth)
|
||||
except Exception as e:
|
||||
if status: status.stop()
|
||||
raise ValueError(f"Engineer failed to connect: {str(e)}")
|
||||
@@ -1409,16 +1432,27 @@ class ai:
|
||||
continue
|
||||
return sorted(sessions, key=lambda x: x["created_at"], reverse=True)
|
||||
|
||||
def list_sessions(self):
|
||||
def list_sessions(self, limit=20):
|
||||
"""Prints a list of sessions using printer.table."""
|
||||
sessions = self._get_sessions()
|
||||
if not sessions:
|
||||
printer.info("No saved AI sessions found.")
|
||||
return
|
||||
|
||||
total = len(sessions)
|
||||
if limit and total > limit:
|
||||
sessions = sessions[:limit]
|
||||
|
||||
columns = ["ID", "Title", "Created At", "Model"]
|
||||
rows = [[s["id"], s["title"], s["created_at"], s["model"]] for s in sessions]
|
||||
printer.table("AI Persisted Sessions", columns, rows)
|
||||
|
||||
title = "AI Persisted Sessions"
|
||||
if limit and total > limit:
|
||||
title += f" (Showing last {limit} of {total})"
|
||||
|
||||
printer.table(title, columns, rows)
|
||||
if limit and total > limit:
|
||||
printer.info(f"Use '--list --all' (if supported) or check the sessions directory to see all {total} sessions.")
|
||||
|
||||
def load_session_data(self, session_id):
|
||||
"""Loads a session's raw data by ID."""
|
||||
@@ -1449,8 +1483,10 @@ class ai:
|
||||
return sessions[0]["id"] if sessions else None
|
||||
|
||||
def _generate_session_id(self, query):
|
||||
"""Generates a unique session ID based on timestamp."""
|
||||
return datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
|
||||
"""Generates a unique session ID based on timestamp and a random suffix."""
|
||||
ts = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
|
||||
suffix = secrets.token_hex(2)
|
||||
return f"{ts}-{suffix}"
|
||||
|
||||
def save_session(self, history, title=None, model=None):
|
||||
"""Saves current history to the session file."""
|
||||
@@ -1459,6 +1495,8 @@ class ai:
|
||||
first_user_msg = next((m["content"] for m in history if m["role"] == "user"), "new-session")
|
||||
self.session_id = self._generate_session_id(first_user_msg)
|
||||
self.session_path = os.path.join(self.sessions_dir, f"{self.session_id}.json")
|
||||
elif not self.session_path:
|
||||
self.session_path = os.path.join(self.sessions_dir, f"{self.session_id}.json")
|
||||
|
||||
# If it's a new file, we might want to set a better title
|
||||
if not os.path.exists(self.session_path) and not title:
|
||||
@@ -1496,16 +1534,22 @@ class ai:
|
||||
|
||||
@MethodHook
|
||||
def ask(self, user_input, dryrun=False, chat_history=None, status=None, debug=False, stream=True, session_id=None, chunk_callback=None):
|
||||
if not self.engineer_key:
|
||||
raise ValueError("Engineer API key not configured. Use 'connpy config --engineer-api-key <key>' to set it.")
|
||||
is_engineer_keyless = "vertex" in self.engineer_model.lower() or "ollama" in self.engineer_model.lower() or "local" in self.engineer_model.lower()
|
||||
if not self.engineer_key and not self.engineer_auth and not is_engineer_keyless:
|
||||
raise ValueError("Engineer API key or authentication not configured. Use 'connpy config --engineer-auth <auth>' to set it.")
|
||||
|
||||
if chat_history is None: chat_history = []
|
||||
|
||||
# Load session if provided and history is empty
|
||||
if session_id and not chat_history:
|
||||
session_data = self.load_session_data(session_id)
|
||||
if session_data:
|
||||
chat_history = session_data.get("history", [])
|
||||
if session_id:
|
||||
# Force the session_id even if it doesn't exist yet
|
||||
self.session_id = session_id
|
||||
self.session_path = os.path.join(self.sessions_dir, f"{session_id}.json")
|
||||
|
||||
if not chat_history:
|
||||
session_data = self.load_session_data(session_id)
|
||||
if session_data:
|
||||
chat_history = session_data.get("history", [])
|
||||
# If we loaded history, the caller might need it back
|
||||
# But typically ask() is called in a loop with an external history object
|
||||
|
||||
@@ -1541,6 +1585,7 @@ class ai:
|
||||
tools = self._get_architect_tools() if current_brain == "architect" else self._get_engineer_tools()
|
||||
model = self.architect_model if current_brain == "architect" else self.engineer_model
|
||||
key = self.architect_key if current_brain == "architect" else self.engineer_key
|
||||
current_auth = self.architect_auth if current_brain == "architect" else self.engineer_auth
|
||||
|
||||
# Estructura optimizada para Prompt Caching (Solo para Anthropic directo, Vertex tiene reglas distintas)
|
||||
if "claude" in model.lower() and "vertex" not in model.lower():
|
||||
@@ -1590,8 +1635,8 @@ class ai:
|
||||
|
||||
label = "[architect][bold]Architect[/bold][/architect]" if current_brain == "architect" else "[engineer][bold]Engineer[/bold][/engineer]"
|
||||
if status:
|
||||
# Notify responder identity ONLY for web/remote clients (StatusBridge has is_web)
|
||||
if getattr(status, "is_web", False):
|
||||
# Notify responder identity for web/remote clients
|
||||
if getattr(status, "is_web", False) or getattr(status, "is_remote", False):
|
||||
status.update(f"__RESPONDER__:{current_brain}")
|
||||
status.update(f"{label} is thinking... (step {iteration})")
|
||||
|
||||
@@ -1600,12 +1645,12 @@ class ai:
|
||||
safe_messages = self._sanitize_messages(messages)
|
||||
if stream:
|
||||
response, streamed_response = self._stream_completion(
|
||||
model=model, messages=safe_messages, tools=tools, api_key=key,
|
||||
model=model, messages=safe_messages, tools=tools, auth=current_auth,
|
||||
status=status, label=label, debug=debug, num_retries=3,
|
||||
chunk_callback=chunk_callback
|
||||
)
|
||||
else:
|
||||
response = completion(model=model, messages=safe_messages, tools=tools, api_key=key, num_retries=3)
|
||||
response = completion(model=model, messages=safe_messages, tools=tools, num_retries=3, **current_auth)
|
||||
except Exception as e:
|
||||
if current_brain == "architect":
|
||||
if status: status.update("[unavailable]Architect unavailable! Falling back to Engineer...")
|
||||
@@ -1614,6 +1659,7 @@ class ai:
|
||||
model = self.engineer_model
|
||||
tools = self._get_engineer_tools()
|
||||
key = self.engineer_key
|
||||
current_auth = self.engineer_auth
|
||||
# Rebuild messages with Engineer system prompt and original user request
|
||||
messages = [{"role": "system", "content": self.engineer_system_prompt}]
|
||||
# Add chat history if exists (excluding system prompt)
|
||||
@@ -1706,6 +1752,7 @@ class ai:
|
||||
model = self.architect_model
|
||||
tools = self._get_architect_tools()
|
||||
key = self.architect_key
|
||||
current_auth = self.architect_auth
|
||||
messages[0] = {"role": "system", "content": self.architect_system_prompt}
|
||||
# Prepare handover context to inject AFTER all tool responses
|
||||
handover_msg = f"HANDOVER FROM EXECUTION ENGINE\n\nReason: {args['reason']}\n\nContext: {args['context']}\n\nYou are now in control of this conversation."
|
||||
@@ -1727,6 +1774,7 @@ class ai:
|
||||
model = self.engineer_model
|
||||
tools = self._get_engineer_tools()
|
||||
key = self.engineer_key
|
||||
current_auth = self.engineer_auth
|
||||
messages[0] = {"role": "system", "content": self.engineer_system_prompt}
|
||||
# Prepare handover context to inject AFTER all tool responses
|
||||
handover_msg = f"HANDOVER FROM ARCHITECT\n\nSummary: {args['summary']}\n\nYou are now back in control. Continue handling the user's requests."
|
||||
@@ -1768,7 +1816,7 @@ class ai:
|
||||
messages.append({"role": "user", "content": "Hard iteration limit reached. Please provide a summary of your findings so far."})
|
||||
try:
|
||||
safe_messages = self._sanitize_messages(messages)
|
||||
response = completion(model=model, messages=safe_messages, tools=[], api_key=key)
|
||||
response = completion(model=model, messages=safe_messages, tools=[], **current_auth)
|
||||
resp_msg = response.choices[0].message
|
||||
messages.append(resp_msg.model_dump(exclude_none=True))
|
||||
except Exception as e:
|
||||
@@ -1788,7 +1836,7 @@ class ai:
|
||||
try:
|
||||
safe_messages = self._sanitize_messages(summary_messages)
|
||||
# Use tools=None to force a text summary during interruption
|
||||
response = completion(model=model, messages=safe_messages, tools=None, api_key=key)
|
||||
response = completion(model=model, messages=safe_messages, tools=None, **current_auth)
|
||||
resp_msg = response.choices[0].message
|
||||
messages.append(resp_msg.model_dump(exclude_none=True))
|
||||
|
||||
@@ -1925,6 +1973,7 @@ Node: {node_name}"""
|
||||
# Use models based on persona
|
||||
current_model = self.architect_model if persona == "architect" else self.engineer_model
|
||||
current_key = self.architect_key if persona == "architect" else self.engineer_key
|
||||
current_auth = self.architect_auth if persona == "architect" else self.engineer_auth
|
||||
|
||||
try:
|
||||
while iteration < max_iterations:
|
||||
@@ -1934,8 +1983,8 @@ Node: {node_name}"""
|
||||
model=current_model,
|
||||
messages=messages,
|
||||
tools=mcp_tools if mcp_tools else None,
|
||||
api_key=current_key,
|
||||
stream=True
|
||||
stream=True,
|
||||
**current_auth
|
||||
)
|
||||
|
||||
full_content = ""
|
||||
@@ -2008,8 +2057,8 @@ Node: {node_name}"""
|
||||
model=self.engineer_model,
|
||||
messages=messages,
|
||||
tools=None,
|
||||
api_key=self.engineer_key,
|
||||
stream=True
|
||||
stream=True,
|
||||
**self.engineer_auth
|
||||
)
|
||||
|
||||
full_content = ""
|
||||
@@ -2095,7 +2144,7 @@ Node: {node_name}"""
|
||||
<dl>
|
||||
<dt id="connpy.ai.SAFE_COMMANDS"><code class="name">var <span class="ident">SAFE_COMMANDS</span></code></dt>
|
||||
<dd>
|
||||
<div class="desc"></div>
|
||||
<div class="desc"><p>The type of the None singleton.</p></div>
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>Instance variables</h3>
|
||||
@@ -2255,6 +2304,7 @@ Node: {node_name}"""
|
||||
# Use models based on persona
|
||||
current_model = self.architect_model if persona == "architect" else self.engineer_model
|
||||
current_key = self.architect_key if persona == "architect" else self.engineer_key
|
||||
current_auth = self.architect_auth if persona == "architect" else self.engineer_auth
|
||||
|
||||
try:
|
||||
while iteration < max_iterations:
|
||||
@@ -2264,8 +2314,8 @@ Node: {node_name}"""
|
||||
model=current_model,
|
||||
messages=messages,
|
||||
tools=mcp_tools if mcp_tools else None,
|
||||
api_key=current_key,
|
||||
stream=True
|
||||
stream=True,
|
||||
**current_auth
|
||||
)
|
||||
|
||||
full_content = ""
|
||||
@@ -2338,8 +2388,8 @@ Node: {node_name}"""
|
||||
model=self.engineer_model,
|
||||
messages=messages,
|
||||
tools=None,
|
||||
api_key=self.engineer_key,
|
||||
stream=True
|
||||
stream=True,
|
||||
**self.engineer_auth
|
||||
)
|
||||
|
||||
full_content = ""
|
||||
@@ -2429,16 +2479,22 @@ Node: {node_name}"""
|
||||
</summary>
|
||||
<pre><code class="python">@MethodHook
|
||||
def ask(self, user_input, dryrun=False, chat_history=None, status=None, debug=False, stream=True, session_id=None, chunk_callback=None):
|
||||
if not self.engineer_key:
|
||||
raise ValueError("Engineer API key not configured. Use 'connpy config --engineer-api-key <key>' to set it.")
|
||||
is_engineer_keyless = "vertex" in self.engineer_model.lower() or "ollama" in self.engineer_model.lower() or "local" in self.engineer_model.lower()
|
||||
if not self.engineer_key and not self.engineer_auth and not is_engineer_keyless:
|
||||
raise ValueError("Engineer API key or authentication not configured. Use 'connpy config --engineer-auth <auth>' to set it.")
|
||||
|
||||
if chat_history is None: chat_history = []
|
||||
|
||||
# Load session if provided and history is empty
|
||||
if session_id and not chat_history:
|
||||
session_data = self.load_session_data(session_id)
|
||||
if session_data:
|
||||
chat_history = session_data.get("history", [])
|
||||
if session_id:
|
||||
# Force the session_id even if it doesn't exist yet
|
||||
self.session_id = session_id
|
||||
self.session_path = os.path.join(self.sessions_dir, f"{session_id}.json")
|
||||
|
||||
if not chat_history:
|
||||
session_data = self.load_session_data(session_id)
|
||||
if session_data:
|
||||
chat_history = session_data.get("history", [])
|
||||
# If we loaded history, the caller might need it back
|
||||
# But typically ask() is called in a loop with an external history object
|
||||
|
||||
@@ -2474,6 +2530,7 @@ def ask(self, user_input, dryrun=False, chat_history=None, status=None, debug=Fa
|
||||
tools = self._get_architect_tools() if current_brain == "architect" else self._get_engineer_tools()
|
||||
model = self.architect_model if current_brain == "architect" else self.engineer_model
|
||||
key = self.architect_key if current_brain == "architect" else self.engineer_key
|
||||
current_auth = self.architect_auth if current_brain == "architect" else self.engineer_auth
|
||||
|
||||
# Estructura optimizada para Prompt Caching (Solo para Anthropic directo, Vertex tiene reglas distintas)
|
||||
if "claude" in model.lower() and "vertex" not in model.lower():
|
||||
@@ -2523,8 +2580,8 @@ def ask(self, user_input, dryrun=False, chat_history=None, status=None, debug=Fa
|
||||
|
||||
label = "[architect][bold]Architect[/bold][/architect]" if current_brain == "architect" else "[engineer][bold]Engineer[/bold][/engineer]"
|
||||
if status:
|
||||
# Notify responder identity ONLY for web/remote clients (StatusBridge has is_web)
|
||||
if getattr(status, "is_web", False):
|
||||
# Notify responder identity for web/remote clients
|
||||
if getattr(status, "is_web", False) or getattr(status, "is_remote", False):
|
||||
status.update(f"__RESPONDER__:{current_brain}")
|
||||
status.update(f"{label} is thinking... (step {iteration})")
|
||||
|
||||
@@ -2533,12 +2590,12 @@ def ask(self, user_input, dryrun=False, chat_history=None, status=None, debug=Fa
|
||||
safe_messages = self._sanitize_messages(messages)
|
||||
if stream:
|
||||
response, streamed_response = self._stream_completion(
|
||||
model=model, messages=safe_messages, tools=tools, api_key=key,
|
||||
model=model, messages=safe_messages, tools=tools, auth=current_auth,
|
||||
status=status, label=label, debug=debug, num_retries=3,
|
||||
chunk_callback=chunk_callback
|
||||
)
|
||||
else:
|
||||
response = completion(model=model, messages=safe_messages, tools=tools, api_key=key, num_retries=3)
|
||||
response = completion(model=model, messages=safe_messages, tools=tools, num_retries=3, **current_auth)
|
||||
except Exception as e:
|
||||
if current_brain == "architect":
|
||||
if status: status.update("[unavailable]Architect unavailable! Falling back to Engineer...")
|
||||
@@ -2547,6 +2604,7 @@ def ask(self, user_input, dryrun=False, chat_history=None, status=None, debug=Fa
|
||||
model = self.engineer_model
|
||||
tools = self._get_engineer_tools()
|
||||
key = self.engineer_key
|
||||
current_auth = self.engineer_auth
|
||||
# Rebuild messages with Engineer system prompt and original user request
|
||||
messages = [{"role": "system", "content": self.engineer_system_prompt}]
|
||||
# Add chat history if exists (excluding system prompt)
|
||||
@@ -2639,6 +2697,7 @@ def ask(self, user_input, dryrun=False, chat_history=None, status=None, debug=Fa
|
||||
model = self.architect_model
|
||||
tools = self._get_architect_tools()
|
||||
key = self.architect_key
|
||||
current_auth = self.architect_auth
|
||||
messages[0] = {"role": "system", "content": self.architect_system_prompt}
|
||||
# Prepare handover context to inject AFTER all tool responses
|
||||
handover_msg = f"HANDOVER FROM EXECUTION ENGINE\n\nReason: {args['reason']}\n\nContext: {args['context']}\n\nYou are now in control of this conversation."
|
||||
@@ -2660,6 +2719,7 @@ def ask(self, user_input, dryrun=False, chat_history=None, status=None, debug=Fa
|
||||
model = self.engineer_model
|
||||
tools = self._get_engineer_tools()
|
||||
key = self.engineer_key
|
||||
current_auth = self.engineer_auth
|
||||
messages[0] = {"role": "system", "content": self.engineer_system_prompt}
|
||||
# Prepare handover context to inject AFTER all tool responses
|
||||
handover_msg = f"HANDOVER FROM ARCHITECT\n\nSummary: {args['summary']}\n\nYou are now back in control. Continue handling the user's requests."
|
||||
@@ -2701,7 +2761,7 @@ def ask(self, user_input, dryrun=False, chat_history=None, status=None, debug=Fa
|
||||
messages.append({"role": "user", "content": "Hard iteration limit reached. Please provide a summary of your findings so far."})
|
||||
try:
|
||||
safe_messages = self._sanitize_messages(messages)
|
||||
response = completion(model=model, messages=safe_messages, tools=[], api_key=key)
|
||||
response = completion(model=model, messages=safe_messages, tools=[], **current_auth)
|
||||
resp_msg = response.choices[0].message
|
||||
messages.append(resp_msg.model_dump(exclude_none=True))
|
||||
except Exception as e:
|
||||
@@ -2721,7 +2781,7 @@ def ask(self, user_input, dryrun=False, chat_history=None, status=None, debug=Fa
|
||||
try:
|
||||
safe_messages = self._sanitize_messages(summary_messages)
|
||||
# Use tools=None to force a text summary during interruption
|
||||
response = completion(model=model, messages=safe_messages, tools=None, api_key=key)
|
||||
response = completion(model=model, messages=safe_messages, tools=None, **current_auth)
|
||||
resp_msg = response.choices[0].message
|
||||
messages.append(resp_msg.model_dump(exclude_none=True))
|
||||
|
||||
@@ -2844,23 +2904,34 @@ def confirm(self, user_input): return True</code></pre>
|
||||
<div class="desc"><p>List nodes matching the filter pattern. Returns metadata for <=5 nodes, names only for more.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.ai.list_sessions"><code class="name flex">
|
||||
<span>def <span class="ident">list_sessions</span></span>(<span>self)</span>
|
||||
<span>def <span class="ident">list_sessions</span></span>(<span>self, limit=20)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def list_sessions(self):
|
||||
<pre><code class="python">def list_sessions(self, limit=20):
|
||||
"""Prints a list of sessions using printer.table."""
|
||||
sessions = self._get_sessions()
|
||||
if not sessions:
|
||||
printer.info("No saved AI sessions found.")
|
||||
return
|
||||
|
||||
total = len(sessions)
|
||||
if limit and total > limit:
|
||||
sessions = sessions[:limit]
|
||||
|
||||
columns = ["ID", "Title", "Created At", "Model"]
|
||||
rows = [[s["id"], s["title"], s["created_at"], s["model"]] for s in sessions]
|
||||
printer.table("AI Persisted Sessions", columns, rows)</code></pre>
|
||||
|
||||
title = "AI Persisted Sessions"
|
||||
if limit and total > limit:
|
||||
title += f" (Showing last {limit} of {total})"
|
||||
|
||||
printer.table(title, columns, rows)
|
||||
if limit and total > limit:
|
||||
printer.info(f"Use '--list --all' (if supported) or check the sessions directory to see all {total} sessions.")</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Prints a list of sessions using printer.table.</p></div>
|
||||
</dd>
|
||||
@@ -3070,6 +3141,8 @@ def confirm(self, user_input): return True</code></pre>
|
||||
first_user_msg = next((m["content"] for m in history if m["role"] == "user"), "new-session")
|
||||
self.session_id = self._generate_session_id(first_user_msg)
|
||||
self.session_path = os.path.join(self.sessions_dir, f"{self.session_id}.json")
|
||||
elif not self.session_path:
|
||||
self.session_path = os.path.join(self.sessions_dir, f"{self.session_id}.json")
|
||||
|
||||
# If it's a new file, we might want to set a better title
|
||||
if not os.path.exists(self.session_path) and not title:
|
||||
@@ -4226,8 +4299,11 @@ class node:
|
||||
|
||||
|
||||
def _setup_interact_environment(self, debug=False, logger=None, async_mode=False):
|
||||
size = re.search('columns=([0-9]+).*lines=([0-9]+)',str(os.get_terminal_size()))
|
||||
self.child.setwinsize(int(size.group(2)),int(size.group(1)))
|
||||
try:
|
||||
size = re.search('columns=([0-9]+).*lines=([0-9]+)',str(os.get_terminal_size()))
|
||||
self.child.setwinsize(int(size.group(2)),int(size.group(1)))
|
||||
except OSError:
|
||||
pass
|
||||
if logger:
|
||||
port_str = f":{self.port}" if self.port and self.protocol not in ["ssm", "kubectl", "docker"] else ""
|
||||
logger("success", f"Connected to {self.unique} at {self.host}{port_str} via: {self.protocol}")
|
||||
@@ -4264,6 +4340,7 @@ class node:
|
||||
|
||||
async def _async_interact_loop(self, local_stream, resize_callback, copilot_handler=None):
|
||||
local_stream.setup(resize_callback=resize_callback)
|
||||
self.current_local_stream = local_stream
|
||||
try:
|
||||
child_fd = self.child.child_fd
|
||||
|
||||
@@ -4346,11 +4423,19 @@ class node:
|
||||
# Remove any stray \x00 bytes and forward normally
|
||||
clean_data = data.replace(b'\x00', b'')
|
||||
if clean_data:
|
||||
# Track command boundaries when user hits Enter
|
||||
if hasattr(self, 'mylog') and (b'\r' in clean_data or b'\n' in clean_data):
|
||||
self.cmd_byte_positions.append((self.mylog.tell(), None))
|
||||
# Track command boundaries when user hits Enter or presses Ctrl+C
|
||||
if hasattr(self, 'mylog') and (b'\r' in clean_data or b'\n' in clean_data or b'\x03' in clean_data):
|
||||
pos = self.mylog.tell()
|
||||
marker_cmd = "CANCELLED" if b'\x03' in clean_data else None
|
||||
self.cmd_byte_positions.append((pos, marker_cmd))
|
||||
if hasattr(self, 'current_local_stream') and self.current_local_stream is not None:
|
||||
try:
|
||||
await self.current_local_stream.write(f'\x1b]133;B;{pos}\x07'.encode())
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try: os.write(child_fd, clean_data)
|
||||
try:
|
||||
os.write(child_fd, clean_data)
|
||||
except OSError:
|
||||
break
|
||||
self.lastinput = time()
|
||||
@@ -4470,6 +4555,7 @@ class node:
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
self.current_local_stream = None
|
||||
local_stream.teardown()
|
||||
|
||||
@MethodHook
|
||||
@@ -4498,6 +4584,11 @@ class node:
|
||||
if cmd != slc and hasattr(self, 'cmd_byte_positions') and self.cmd_byte_positions is not None:
|
||||
log_pos = self.mylog.tell() if hasattr(self, 'mylog') else 0
|
||||
self.cmd_byte_positions.append((log_pos, cmd))
|
||||
if hasattr(self, 'current_local_stream') and self.current_local_stream is not None:
|
||||
try:
|
||||
await self.current_local_stream.write(f'\x1b]133;B;{log_pos}\x07'.encode())
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Write physically to PTY
|
||||
os.write(child_fd, (cmd + "\n").encode())
|
||||
@@ -5137,6 +5228,11 @@ async def inject_commands(self, commands, child_fd, on_inject=None):
|
||||
if cmd != slc and hasattr(self, 'cmd_byte_positions') and self.cmd_byte_positions is not None:
|
||||
log_pos = self.mylog.tell() if hasattr(self, 'mylog') else 0
|
||||
self.cmd_byte_positions.append((log_pos, cmd))
|
||||
if hasattr(self, 'current_local_stream') and self.current_local_stream is not None:
|
||||
try:
|
||||
await self.current_local_stream.write(f'\x1b]133;B;{log_pos}\x07'.encode())
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Write physically to PTY
|
||||
os.write(child_fd, (cmd + "\n").encode())
|
||||
@@ -6225,7 +6321,6 @@ def test(self, commands, expected, vars = None,*, folder = None, prompt = None,
|
||||
<li><code><a title="connpy.mcp_client" href="mcp_client.html">connpy.mcp_client</a></code></li>
|
||||
<li><code><a title="connpy.proto" href="proto/index.html">connpy.proto</a></code></li>
|
||||
<li><code><a title="connpy.services" href="services/index.html">connpy.services</a></code></li>
|
||||
<li><code><a title="connpy.tests" href="tests/index.html">connpy.tests</a></code></li>
|
||||
<li><code><a title="connpy.tunnels" href="tunnels.html">connpy.tunnels</a></code></li>
|
||||
<li><code><a title="connpy.utils" href="utils.html">connpy.utils</a></code></li>
|
||||
</ul>
|
||||
@@ -6289,7 +6384,7 @@ def test(self, commands, expected, vars = None,*, folder = None, prompt = None,
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.mcp_client API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -343,7 +343,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.proto API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -60,7 +60,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.services.ai_service API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -58,6 +58,37 @@ el.replaceWith(d);
|
||||
<pre><code class="python">class AIService(BaseService):
|
||||
"""Business logic for interacting with AI agents and LLM configurations."""
|
||||
|
||||
def _clean_cisco_scrolling(self, text: str) -> str:
|
||||
"""Resolves horizontal scrolling artifacts (backspaces, \r, ANSI) by merging overlapping segments."""
|
||||
def merge_overlapping(s1, s2):
|
||||
s2_clean = s2.lstrip(' $')
|
||||
max_overlap = min(len(s1), len(s2_clean))
|
||||
for i in range(max_overlap, 0, -1):
|
||||
if s1[-i:] == s2_clean[:i]:
|
||||
return s1 + s2_clean[i:]
|
||||
return s1 + s2_clean
|
||||
|
||||
scroll_re = re.compile(r'(\x08{5,}\s*\$?|\$\r|\x1b\[\d+[GD]\s*\$?)')
|
||||
parts = scroll_re.split(text)
|
||||
merged = ""
|
||||
|
||||
for part in parts:
|
||||
if scroll_re.match(part):
|
||||
continue
|
||||
|
||||
cleaned = log_cleaner(part)
|
||||
if not merged:
|
||||
merged = cleaned
|
||||
else:
|
||||
merged_lines = merged.split('\n')
|
||||
cleaned_lines = cleaned.split('\n')
|
||||
|
||||
merged_lines[-1] = merge_overlapping(merged_lines[-1], cleaned_lines[0])
|
||||
merged_lines.extend(cleaned_lines[1:])
|
||||
merged = "\n".join(merged_lines)
|
||||
|
||||
return merged
|
||||
|
||||
def build_context_blocks(self, raw_bytes: bytes, cmd_byte_positions: list, node_info: dict, last_line: str = "") -> list:
|
||||
"""Identifies command blocks in the terminal history."""
|
||||
blocks = []
|
||||
@@ -79,28 +110,69 @@ el.replaceWith(d);
|
||||
prev_pos = cmd_byte_positions[i-1][0]
|
||||
|
||||
if known_cmd:
|
||||
prev_chunk = raw_bytes[prev_pos:pos]
|
||||
prev_cleaned = log_cleaner(prev_chunk.decode(errors='replace'))
|
||||
prev_lines = [l for l in prev_cleaned.split('\n') if l.strip()]
|
||||
prompt_text = prev_lines[-1].strip() if prev_lines else ""
|
||||
preview = f"{prompt_text}{known_cmd}" if prompt_text else known_cmd
|
||||
parsed_positions.append({"pos": pos, "type": "VALID_CMD", "preview": preview[:80]})
|
||||
if known_cmd == "CANCELLED":
|
||||
parsed_positions.append({"pos": pos, "type": "CANCELLED", "preview": ""})
|
||||
else:
|
||||
prev_chunk = raw_bytes[prev_pos:pos]
|
||||
prev_cleaned = self._clean_cisco_scrolling(prev_chunk.decode(errors='replace'))
|
||||
prev_lines = [l for l in prev_cleaned.split('\n') if l.strip()]
|
||||
prompt_text = prev_lines[-1].strip() if prev_lines else ""
|
||||
preview = f"{prompt_text}{known_cmd}" if prompt_text else known_cmd
|
||||
|
||||
if len(preview) > 80:
|
||||
preview = preview[:77] + "..."
|
||||
parsed_positions.append({"pos": pos, "type": "VALID_CMD", "preview": preview})
|
||||
else:
|
||||
chunk = raw_bytes[prev_pos:pos]
|
||||
cleaned = log_cleaner(chunk.decode(errors='replace'))
|
||||
lines = [l for l in cleaned.split('\n') if l.strip()]
|
||||
preview = lines[-1].strip() if lines else ""
|
||||
|
||||
if preview:
|
||||
match = prompt_re.search(preview)
|
||||
if match:
|
||||
cmd_text = preview[match.end():].strip()
|
||||
if cmd_text:
|
||||
parsed_positions.append({"pos": pos, "type": "VALID_CMD", "preview": preview[:80]})
|
||||
else:
|
||||
parsed_positions.append({"pos": pos, "type": "EMPTY_PROMPT", "preview": ""})
|
||||
else:
|
||||
parsed_positions.append({"pos": pos, "type": "SCROLLING", "preview": ""})
|
||||
cleaned = self._clean_cisco_scrolling(chunk.decode(errors='replace'))
|
||||
lines = [l for l in cleaned.split('\n') if l.strip()]
|
||||
|
||||
found_in_pass1 = False
|
||||
if lines:
|
||||
# Search backwards through the last few lines for the prompt
|
||||
for idx in range(len(lines) - 1, max(-1, len(lines) - 10), -1):
|
||||
match = prompt_re.search(lines[idx])
|
||||
if match:
|
||||
ptxt = match.group(0).strip()
|
||||
cmd_first_line = lines[idx][match.end():].strip()
|
||||
cmd_rest = [l.strip() for l in lines[idx+1:]]
|
||||
cmd_text = " ".join([cmd_first_line] + cmd_rest).strip()
|
||||
|
||||
if cmd_text:
|
||||
pv = f"{ptxt} {cmd_text}".strip()
|
||||
if len(pv) > 80:
|
||||
pv = pv[:77] + "..."
|
||||
parsed_positions.append({"pos": pos, "type": "VALID_CMD", "preview": pv})
|
||||
else:
|
||||
parsed_positions.append({"pos": pos, "type": "EMPTY_PROMPT", "preview": ""})
|
||||
found_in_pass1 = True
|
||||
break
|
||||
|
||||
if not found_in_pass1:
|
||||
# Fallback: The prompt might have been isolated in the previous chunk
|
||||
# due to asynchronous network delays splitting the output exactly at the newline.
|
||||
prev_was_valid_cmd = i >= 2 and parsed_positions[i-2]["type"] == "VALID_CMD"
|
||||
if prev_pos > 0 and not prev_was_valid_cmd:
|
||||
# Fetch the very last chunk that we just processed
|
||||
prev_prev_pos = cmd_byte_positions[i-2][0] if i >= 2 else 0
|
||||
prev_chunk_text = self._clean_cisco_scrolling(raw_bytes[prev_prev_pos:prev_pos].decode(errors='replace'))
|
||||
prev_lines_text = [l for l in prev_chunk_text.split('\n') if l.strip()]
|
||||
|
||||
if prev_lines_text:
|
||||
prev_match = prompt_re.search(prev_lines_text[-1])
|
||||
if prev_match:
|
||||
ptxt = prev_match.group(0).strip()
|
||||
cmd_text = " ".join([l.strip() for l in lines]).strip()
|
||||
if cmd_text:
|
||||
pv = f"{ptxt} {cmd_text}".strip()
|
||||
if len(pv) > 80:
|
||||
pv = pv[:77] + "..."
|
||||
parsed_positions.append({"pos": pos, "type": "VALID_CMD", "preview": pv})
|
||||
found_in_pass1 = True
|
||||
|
||||
if not found_in_pass1:
|
||||
parsed_positions.append({"pos": pos, "type": "SCROLLING", "preview": ""})
|
||||
else:
|
||||
parsed_positions.append({"pos": pos, "type": "SCROLLING", "preview": ""})
|
||||
|
||||
@@ -113,11 +185,11 @@ el.replaceWith(d);
|
||||
start_pos = item["pos"]
|
||||
preview = item["preview"]
|
||||
|
||||
# Find the end position: next VALID_CMD or EMPTY_PROMPT
|
||||
# Find the end position: next VALID_CMD or EMPTY_PROMPT or CANCELLED
|
||||
end_pos = current_prompt_pos
|
||||
for j in range(i + 1, len(parsed_positions)):
|
||||
next_item = parsed_positions[j]
|
||||
if next_item["type"] in ("VALID_CMD", "EMPTY_PROMPT"):
|
||||
if next_item["type"] in ("VALID_CMD", "EMPTY_PROMPT", "CANCELLED"):
|
||||
end_pos = next_item["pos"]
|
||||
break
|
||||
|
||||
@@ -219,11 +291,14 @@ el.replaceWith(d);
|
||||
return await asyncio.wrap_future(future)
|
||||
|
||||
|
||||
def list_sessions(self):
|
||||
"""Return a list of all saved AI sessions."""
|
||||
def list_sessions(self, limit=None):
|
||||
"""Return a list of saved AI sessions, optionally limited."""
|
||||
from connpy.ai import ai
|
||||
agent = ai(self.config)
|
||||
return agent._get_sessions()
|
||||
sessions = agent._get_sessions()
|
||||
if limit and len(sessions) > limit:
|
||||
return sessions[:limit], len(sessions)
|
||||
return sessions, len(sessions)
|
||||
|
||||
def delete_session(self, session_id):
|
||||
"""Delete an AI session by ID."""
|
||||
@@ -235,13 +310,15 @@ el.replaceWith(d);
|
||||
else:
|
||||
raise InvalidConfigurationError(f"Session '{session_id}' not found.")
|
||||
|
||||
def configure_provider(self, provider, model=None, api_key=None):
|
||||
def configure_provider(self, provider, model=None, api_key=None, auth=None):
|
||||
"""Update AI provider settings in the configuration."""
|
||||
settings = self.config.config.get("ai", {})
|
||||
if model:
|
||||
settings[f"{provider}_model"] = model
|
||||
if api_key:
|
||||
settings[f"{provider}_api_key"] = api_key
|
||||
if auth is not None:
|
||||
settings[f"{provider}_auth"] = auth
|
||||
|
||||
self.config.config["ai"] = settings
|
||||
self.config._saveconfig(self.config.file)
|
||||
@@ -280,6 +357,11 @@ el.replaceWith(d);
|
||||
self.config.config["ai"] = ai_settings
|
||||
self.config._saveconfig(self.config.file)
|
||||
|
||||
def list_mcp_servers(self) -> dict:
|
||||
"""Get the configured MCP servers."""
|
||||
ai_settings = self.config.config.get("ai", {})
|
||||
return ai_settings.get("mcp_servers", {})
|
||||
|
||||
def load_session_data(self, session_id):
|
||||
"""Load a session's raw data by ID."""
|
||||
from connpy.ai import ai
|
||||
@@ -379,28 +461,69 @@ el.replaceWith(d);
|
||||
prev_pos = cmd_byte_positions[i-1][0]
|
||||
|
||||
if known_cmd:
|
||||
prev_chunk = raw_bytes[prev_pos:pos]
|
||||
prev_cleaned = log_cleaner(prev_chunk.decode(errors='replace'))
|
||||
prev_lines = [l for l in prev_cleaned.split('\n') if l.strip()]
|
||||
prompt_text = prev_lines[-1].strip() if prev_lines else ""
|
||||
preview = f"{prompt_text}{known_cmd}" if prompt_text else known_cmd
|
||||
parsed_positions.append({"pos": pos, "type": "VALID_CMD", "preview": preview[:80]})
|
||||
if known_cmd == "CANCELLED":
|
||||
parsed_positions.append({"pos": pos, "type": "CANCELLED", "preview": ""})
|
||||
else:
|
||||
prev_chunk = raw_bytes[prev_pos:pos]
|
||||
prev_cleaned = self._clean_cisco_scrolling(prev_chunk.decode(errors='replace'))
|
||||
prev_lines = [l for l in prev_cleaned.split('\n') if l.strip()]
|
||||
prompt_text = prev_lines[-1].strip() if prev_lines else ""
|
||||
preview = f"{prompt_text}{known_cmd}" if prompt_text else known_cmd
|
||||
|
||||
if len(preview) > 80:
|
||||
preview = preview[:77] + "..."
|
||||
parsed_positions.append({"pos": pos, "type": "VALID_CMD", "preview": preview})
|
||||
else:
|
||||
chunk = raw_bytes[prev_pos:pos]
|
||||
cleaned = log_cleaner(chunk.decode(errors='replace'))
|
||||
lines = [l for l in cleaned.split('\n') if l.strip()]
|
||||
preview = lines[-1].strip() if lines else ""
|
||||
|
||||
if preview:
|
||||
match = prompt_re.search(preview)
|
||||
if match:
|
||||
cmd_text = preview[match.end():].strip()
|
||||
if cmd_text:
|
||||
parsed_positions.append({"pos": pos, "type": "VALID_CMD", "preview": preview[:80]})
|
||||
else:
|
||||
parsed_positions.append({"pos": pos, "type": "EMPTY_PROMPT", "preview": ""})
|
||||
else:
|
||||
parsed_positions.append({"pos": pos, "type": "SCROLLING", "preview": ""})
|
||||
cleaned = self._clean_cisco_scrolling(chunk.decode(errors='replace'))
|
||||
lines = [l for l in cleaned.split('\n') if l.strip()]
|
||||
|
||||
found_in_pass1 = False
|
||||
if lines:
|
||||
# Search backwards through the last few lines for the prompt
|
||||
for idx in range(len(lines) - 1, max(-1, len(lines) - 10), -1):
|
||||
match = prompt_re.search(lines[idx])
|
||||
if match:
|
||||
ptxt = match.group(0).strip()
|
||||
cmd_first_line = lines[idx][match.end():].strip()
|
||||
cmd_rest = [l.strip() for l in lines[idx+1:]]
|
||||
cmd_text = " ".join([cmd_first_line] + cmd_rest).strip()
|
||||
|
||||
if cmd_text:
|
||||
pv = f"{ptxt} {cmd_text}".strip()
|
||||
if len(pv) > 80:
|
||||
pv = pv[:77] + "..."
|
||||
parsed_positions.append({"pos": pos, "type": "VALID_CMD", "preview": pv})
|
||||
else:
|
||||
parsed_positions.append({"pos": pos, "type": "EMPTY_PROMPT", "preview": ""})
|
||||
found_in_pass1 = True
|
||||
break
|
||||
|
||||
if not found_in_pass1:
|
||||
# Fallback: The prompt might have been isolated in the previous chunk
|
||||
# due to asynchronous network delays splitting the output exactly at the newline.
|
||||
prev_was_valid_cmd = i >= 2 and parsed_positions[i-2]["type"] == "VALID_CMD"
|
||||
if prev_pos > 0 and not prev_was_valid_cmd:
|
||||
# Fetch the very last chunk that we just processed
|
||||
prev_prev_pos = cmd_byte_positions[i-2][0] if i >= 2 else 0
|
||||
prev_chunk_text = self._clean_cisco_scrolling(raw_bytes[prev_prev_pos:prev_pos].decode(errors='replace'))
|
||||
prev_lines_text = [l for l in prev_chunk_text.split('\n') if l.strip()]
|
||||
|
||||
if prev_lines_text:
|
||||
prev_match = prompt_re.search(prev_lines_text[-1])
|
||||
if prev_match:
|
||||
ptxt = prev_match.group(0).strip()
|
||||
cmd_text = " ".join([l.strip() for l in lines]).strip()
|
||||
if cmd_text:
|
||||
pv = f"{ptxt} {cmd_text}".strip()
|
||||
if len(pv) > 80:
|
||||
pv = pv[:77] + "..."
|
||||
parsed_positions.append({"pos": pos, "type": "VALID_CMD", "preview": pv})
|
||||
found_in_pass1 = True
|
||||
|
||||
if not found_in_pass1:
|
||||
parsed_positions.append({"pos": pos, "type": "SCROLLING", "preview": ""})
|
||||
else:
|
||||
parsed_positions.append({"pos": pos, "type": "SCROLLING", "preview": ""})
|
||||
|
||||
@@ -413,11 +536,11 @@ el.replaceWith(d);
|
||||
start_pos = item["pos"]
|
||||
preview = item["preview"]
|
||||
|
||||
# Find the end position: next VALID_CMD or EMPTY_PROMPT
|
||||
# Find the end position: next VALID_CMD or EMPTY_PROMPT or CANCELLED
|
||||
end_pos = current_prompt_pos
|
||||
for j in range(i + 1, len(parsed_positions)):
|
||||
next_item = parsed_positions[j]
|
||||
if next_item["type"] in ("VALID_CMD", "EMPTY_PROMPT"):
|
||||
if next_item["type"] in ("VALID_CMD", "EMPTY_PROMPT", "CANCELLED"):
|
||||
end_pos = next_item["pos"]
|
||||
break
|
||||
|
||||
@@ -478,20 +601,22 @@ el.replaceWith(d);
|
||||
<div class="desc"><p>Update MCP server settings in the configuration with smart merging.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.ai_service.AIService.configure_provider"><code class="name flex">
|
||||
<span>def <span class="ident">configure_provider</span></span>(<span>self, provider, model=None, api_key=None)</span>
|
||||
<span>def <span class="ident">configure_provider</span></span>(<span>self, provider, model=None, api_key=None, auth=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def configure_provider(self, provider, model=None, api_key=None):
|
||||
<pre><code class="python">def configure_provider(self, provider, model=None, api_key=None, auth=None):
|
||||
"""Update AI provider settings in the configuration."""
|
||||
settings = self.config.config.get("ai", {})
|
||||
if model:
|
||||
settings[f"{provider}_model"] = model
|
||||
if api_key:
|
||||
settings[f"{provider}_api_key"] = api_key
|
||||
if auth is not None:
|
||||
settings[f"{provider}_auth"] = auth
|
||||
|
||||
self.config.config["ai"] = settings
|
||||
self.config._saveconfig(self.config.file)</code></pre>
|
||||
@@ -534,21 +659,39 @@ el.replaceWith(d);
|
||||
</details>
|
||||
<div class="desc"><p>Delete an AI session by ID.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.ai_service.AIService.list_sessions"><code class="name flex">
|
||||
<span>def <span class="ident">list_sessions</span></span>(<span>self)</span>
|
||||
<dt id="connpy.services.ai_service.AIService.list_mcp_servers"><code class="name flex">
|
||||
<span>def <span class="ident">list_mcp_servers</span></span>(<span>self) ‑> dict</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def list_sessions(self):
|
||||
"""Return a list of all saved AI sessions."""
|
||||
<pre><code class="python">def list_mcp_servers(self) -> dict:
|
||||
"""Get the configured MCP servers."""
|
||||
ai_settings = self.config.config.get("ai", {})
|
||||
return ai_settings.get("mcp_servers", {})</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Get the configured MCP servers.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.ai_service.AIService.list_sessions"><code class="name flex">
|
||||
<span>def <span class="ident">list_sessions</span></span>(<span>self, limit=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def list_sessions(self, limit=None):
|
||||
"""Return a list of saved AI sessions, optionally limited."""
|
||||
from connpy.ai import ai
|
||||
agent = ai(self.config)
|
||||
return agent._get_sessions()</code></pre>
|
||||
sessions = agent._get_sessions()
|
||||
if limit and len(sessions) > limit:
|
||||
return sessions[:limit], len(sessions)
|
||||
return sessions, len(sessions)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Return a list of all saved AI sessions.</p></div>
|
||||
<div class="desc"><p>Return a list of saved AI sessions, optionally limited.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.ai_service.AIService.load_session_data"><code class="name flex">
|
||||
<span>def <span class="ident">load_session_data</span></span>(<span>self, session_id)</span>
|
||||
@@ -671,6 +814,7 @@ el.replaceWith(d);
|
||||
<li><code><a title="connpy.services.ai_service.AIService.configure_provider" href="#connpy.services.ai_service.AIService.configure_provider">configure_provider</a></code></li>
|
||||
<li><code><a title="connpy.services.ai_service.AIService.confirm" href="#connpy.services.ai_service.AIService.confirm">confirm</a></code></li>
|
||||
<li><code><a title="connpy.services.ai_service.AIService.delete_session" href="#connpy.services.ai_service.AIService.delete_session">delete_session</a></code></li>
|
||||
<li><code><a title="connpy.services.ai_service.AIService.list_mcp_servers" href="#connpy.services.ai_service.AIService.list_mcp_servers">list_mcp_servers</a></code></li>
|
||||
<li><code><a title="connpy.services.ai_service.AIService.list_sessions" href="#connpy.services.ai_service.AIService.list_sessions">list_sessions</a></code></li>
|
||||
<li><code><a title="connpy.services.ai_service.AIService.load_session_data" href="#connpy.services.ai_service.AIService.load_session_data">load_session_data</a></code></li>
|
||||
<li><code><a title="connpy.services.ai_service.AIService.process_copilot_input" href="#connpy.services.ai_service.AIService.process_copilot_input">process_copilot_input</a></code></li>
|
||||
@@ -682,7 +826,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.services.base API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -152,7 +152,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.services.config_service API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -319,7 +319,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.services.context_service API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -370,7 +370,7 @@ def current_context(self) -> str:
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.services.exceptions API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -268,7 +268,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.services.execution_service API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -449,7 +449,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.services.import_export_service API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -361,7 +361,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+200
-56
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.services API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -113,6 +113,37 @@ el.replaceWith(d);
|
||||
<pre><code class="python">class AIService(BaseService):
|
||||
"""Business logic for interacting with AI agents and LLM configurations."""
|
||||
|
||||
def _clean_cisco_scrolling(self, text: str) -> str:
|
||||
"""Resolves horizontal scrolling artifacts (backspaces, \r, ANSI) by merging overlapping segments."""
|
||||
def merge_overlapping(s1, s2):
|
||||
s2_clean = s2.lstrip(' $')
|
||||
max_overlap = min(len(s1), len(s2_clean))
|
||||
for i in range(max_overlap, 0, -1):
|
||||
if s1[-i:] == s2_clean[:i]:
|
||||
return s1 + s2_clean[i:]
|
||||
return s1 + s2_clean
|
||||
|
||||
scroll_re = re.compile(r'(\x08{5,}\s*\$?|\$\r|\x1b\[\d+[GD]\s*\$?)')
|
||||
parts = scroll_re.split(text)
|
||||
merged = ""
|
||||
|
||||
for part in parts:
|
||||
if scroll_re.match(part):
|
||||
continue
|
||||
|
||||
cleaned = log_cleaner(part)
|
||||
if not merged:
|
||||
merged = cleaned
|
||||
else:
|
||||
merged_lines = merged.split('\n')
|
||||
cleaned_lines = cleaned.split('\n')
|
||||
|
||||
merged_lines[-1] = merge_overlapping(merged_lines[-1], cleaned_lines[0])
|
||||
merged_lines.extend(cleaned_lines[1:])
|
||||
merged = "\n".join(merged_lines)
|
||||
|
||||
return merged
|
||||
|
||||
def build_context_blocks(self, raw_bytes: bytes, cmd_byte_positions: list, node_info: dict, last_line: str = "") -> list:
|
||||
"""Identifies command blocks in the terminal history."""
|
||||
blocks = []
|
||||
@@ -134,28 +165,69 @@ el.replaceWith(d);
|
||||
prev_pos = cmd_byte_positions[i-1][0]
|
||||
|
||||
if known_cmd:
|
||||
prev_chunk = raw_bytes[prev_pos:pos]
|
||||
prev_cleaned = log_cleaner(prev_chunk.decode(errors='replace'))
|
||||
prev_lines = [l for l in prev_cleaned.split('\n') if l.strip()]
|
||||
prompt_text = prev_lines[-1].strip() if prev_lines else ""
|
||||
preview = f"{prompt_text}{known_cmd}" if prompt_text else known_cmd
|
||||
parsed_positions.append({"pos": pos, "type": "VALID_CMD", "preview": preview[:80]})
|
||||
if known_cmd == "CANCELLED":
|
||||
parsed_positions.append({"pos": pos, "type": "CANCELLED", "preview": ""})
|
||||
else:
|
||||
prev_chunk = raw_bytes[prev_pos:pos]
|
||||
prev_cleaned = self._clean_cisco_scrolling(prev_chunk.decode(errors='replace'))
|
||||
prev_lines = [l for l in prev_cleaned.split('\n') if l.strip()]
|
||||
prompt_text = prev_lines[-1].strip() if prev_lines else ""
|
||||
preview = f"{prompt_text}{known_cmd}" if prompt_text else known_cmd
|
||||
|
||||
if len(preview) > 80:
|
||||
preview = preview[:77] + "..."
|
||||
parsed_positions.append({"pos": pos, "type": "VALID_CMD", "preview": preview})
|
||||
else:
|
||||
chunk = raw_bytes[prev_pos:pos]
|
||||
cleaned = log_cleaner(chunk.decode(errors='replace'))
|
||||
lines = [l for l in cleaned.split('\n') if l.strip()]
|
||||
preview = lines[-1].strip() if lines else ""
|
||||
|
||||
if preview:
|
||||
match = prompt_re.search(preview)
|
||||
if match:
|
||||
cmd_text = preview[match.end():].strip()
|
||||
if cmd_text:
|
||||
parsed_positions.append({"pos": pos, "type": "VALID_CMD", "preview": preview[:80]})
|
||||
else:
|
||||
parsed_positions.append({"pos": pos, "type": "EMPTY_PROMPT", "preview": ""})
|
||||
else:
|
||||
parsed_positions.append({"pos": pos, "type": "SCROLLING", "preview": ""})
|
||||
cleaned = self._clean_cisco_scrolling(chunk.decode(errors='replace'))
|
||||
lines = [l for l in cleaned.split('\n') if l.strip()]
|
||||
|
||||
found_in_pass1 = False
|
||||
if lines:
|
||||
# Search backwards through the last few lines for the prompt
|
||||
for idx in range(len(lines) - 1, max(-1, len(lines) - 10), -1):
|
||||
match = prompt_re.search(lines[idx])
|
||||
if match:
|
||||
ptxt = match.group(0).strip()
|
||||
cmd_first_line = lines[idx][match.end():].strip()
|
||||
cmd_rest = [l.strip() for l in lines[idx+1:]]
|
||||
cmd_text = " ".join([cmd_first_line] + cmd_rest).strip()
|
||||
|
||||
if cmd_text:
|
||||
pv = f"{ptxt} {cmd_text}".strip()
|
||||
if len(pv) > 80:
|
||||
pv = pv[:77] + "..."
|
||||
parsed_positions.append({"pos": pos, "type": "VALID_CMD", "preview": pv})
|
||||
else:
|
||||
parsed_positions.append({"pos": pos, "type": "EMPTY_PROMPT", "preview": ""})
|
||||
found_in_pass1 = True
|
||||
break
|
||||
|
||||
if not found_in_pass1:
|
||||
# Fallback: The prompt might have been isolated in the previous chunk
|
||||
# due to asynchronous network delays splitting the output exactly at the newline.
|
||||
prev_was_valid_cmd = i >= 2 and parsed_positions[i-2]["type"] == "VALID_CMD"
|
||||
if prev_pos > 0 and not prev_was_valid_cmd:
|
||||
# Fetch the very last chunk that we just processed
|
||||
prev_prev_pos = cmd_byte_positions[i-2][0] if i >= 2 else 0
|
||||
prev_chunk_text = self._clean_cisco_scrolling(raw_bytes[prev_prev_pos:prev_pos].decode(errors='replace'))
|
||||
prev_lines_text = [l for l in prev_chunk_text.split('\n') if l.strip()]
|
||||
|
||||
if prev_lines_text:
|
||||
prev_match = prompt_re.search(prev_lines_text[-1])
|
||||
if prev_match:
|
||||
ptxt = prev_match.group(0).strip()
|
||||
cmd_text = " ".join([l.strip() for l in lines]).strip()
|
||||
if cmd_text:
|
||||
pv = f"{ptxt} {cmd_text}".strip()
|
||||
if len(pv) > 80:
|
||||
pv = pv[:77] + "..."
|
||||
parsed_positions.append({"pos": pos, "type": "VALID_CMD", "preview": pv})
|
||||
found_in_pass1 = True
|
||||
|
||||
if not found_in_pass1:
|
||||
parsed_positions.append({"pos": pos, "type": "SCROLLING", "preview": ""})
|
||||
else:
|
||||
parsed_positions.append({"pos": pos, "type": "SCROLLING", "preview": ""})
|
||||
|
||||
@@ -168,11 +240,11 @@ el.replaceWith(d);
|
||||
start_pos = item["pos"]
|
||||
preview = item["preview"]
|
||||
|
||||
# Find the end position: next VALID_CMD or EMPTY_PROMPT
|
||||
# Find the end position: next VALID_CMD or EMPTY_PROMPT or CANCELLED
|
||||
end_pos = current_prompt_pos
|
||||
for j in range(i + 1, len(parsed_positions)):
|
||||
next_item = parsed_positions[j]
|
||||
if next_item["type"] in ("VALID_CMD", "EMPTY_PROMPT"):
|
||||
if next_item["type"] in ("VALID_CMD", "EMPTY_PROMPT", "CANCELLED"):
|
||||
end_pos = next_item["pos"]
|
||||
break
|
||||
|
||||
@@ -274,11 +346,14 @@ el.replaceWith(d);
|
||||
return await asyncio.wrap_future(future)
|
||||
|
||||
|
||||
def list_sessions(self):
|
||||
"""Return a list of all saved AI sessions."""
|
||||
def list_sessions(self, limit=None):
|
||||
"""Return a list of saved AI sessions, optionally limited."""
|
||||
from connpy.ai import ai
|
||||
agent = ai(self.config)
|
||||
return agent._get_sessions()
|
||||
sessions = agent._get_sessions()
|
||||
if limit and len(sessions) > limit:
|
||||
return sessions[:limit], len(sessions)
|
||||
return sessions, len(sessions)
|
||||
|
||||
def delete_session(self, session_id):
|
||||
"""Delete an AI session by ID."""
|
||||
@@ -290,13 +365,15 @@ el.replaceWith(d);
|
||||
else:
|
||||
raise InvalidConfigurationError(f"Session '{session_id}' not found.")
|
||||
|
||||
def configure_provider(self, provider, model=None, api_key=None):
|
||||
def configure_provider(self, provider, model=None, api_key=None, auth=None):
|
||||
"""Update AI provider settings in the configuration."""
|
||||
settings = self.config.config.get("ai", {})
|
||||
if model:
|
||||
settings[f"{provider}_model"] = model
|
||||
if api_key:
|
||||
settings[f"{provider}_api_key"] = api_key
|
||||
if auth is not None:
|
||||
settings[f"{provider}_auth"] = auth
|
||||
|
||||
self.config.config["ai"] = settings
|
||||
self.config._saveconfig(self.config.file)
|
||||
@@ -335,6 +412,11 @@ el.replaceWith(d);
|
||||
self.config.config["ai"] = ai_settings
|
||||
self.config._saveconfig(self.config.file)
|
||||
|
||||
def list_mcp_servers(self) -> dict:
|
||||
"""Get the configured MCP servers."""
|
||||
ai_settings = self.config.config.get("ai", {})
|
||||
return ai_settings.get("mcp_servers", {})
|
||||
|
||||
def load_session_data(self, session_id):
|
||||
"""Load a session's raw data by ID."""
|
||||
from connpy.ai import ai
|
||||
@@ -434,28 +516,69 @@ el.replaceWith(d);
|
||||
prev_pos = cmd_byte_positions[i-1][0]
|
||||
|
||||
if known_cmd:
|
||||
prev_chunk = raw_bytes[prev_pos:pos]
|
||||
prev_cleaned = log_cleaner(prev_chunk.decode(errors='replace'))
|
||||
prev_lines = [l for l in prev_cleaned.split('\n') if l.strip()]
|
||||
prompt_text = prev_lines[-1].strip() if prev_lines else ""
|
||||
preview = f"{prompt_text}{known_cmd}" if prompt_text else known_cmd
|
||||
parsed_positions.append({"pos": pos, "type": "VALID_CMD", "preview": preview[:80]})
|
||||
if known_cmd == "CANCELLED":
|
||||
parsed_positions.append({"pos": pos, "type": "CANCELLED", "preview": ""})
|
||||
else:
|
||||
prev_chunk = raw_bytes[prev_pos:pos]
|
||||
prev_cleaned = self._clean_cisco_scrolling(prev_chunk.decode(errors='replace'))
|
||||
prev_lines = [l for l in prev_cleaned.split('\n') if l.strip()]
|
||||
prompt_text = prev_lines[-1].strip() if prev_lines else ""
|
||||
preview = f"{prompt_text}{known_cmd}" if prompt_text else known_cmd
|
||||
|
||||
if len(preview) > 80:
|
||||
preview = preview[:77] + "..."
|
||||
parsed_positions.append({"pos": pos, "type": "VALID_CMD", "preview": preview})
|
||||
else:
|
||||
chunk = raw_bytes[prev_pos:pos]
|
||||
cleaned = log_cleaner(chunk.decode(errors='replace'))
|
||||
lines = [l for l in cleaned.split('\n') if l.strip()]
|
||||
preview = lines[-1].strip() if lines else ""
|
||||
|
||||
if preview:
|
||||
match = prompt_re.search(preview)
|
||||
if match:
|
||||
cmd_text = preview[match.end():].strip()
|
||||
if cmd_text:
|
||||
parsed_positions.append({"pos": pos, "type": "VALID_CMD", "preview": preview[:80]})
|
||||
else:
|
||||
parsed_positions.append({"pos": pos, "type": "EMPTY_PROMPT", "preview": ""})
|
||||
else:
|
||||
parsed_positions.append({"pos": pos, "type": "SCROLLING", "preview": ""})
|
||||
cleaned = self._clean_cisco_scrolling(chunk.decode(errors='replace'))
|
||||
lines = [l for l in cleaned.split('\n') if l.strip()]
|
||||
|
||||
found_in_pass1 = False
|
||||
if lines:
|
||||
# Search backwards through the last few lines for the prompt
|
||||
for idx in range(len(lines) - 1, max(-1, len(lines) - 10), -1):
|
||||
match = prompt_re.search(lines[idx])
|
||||
if match:
|
||||
ptxt = match.group(0).strip()
|
||||
cmd_first_line = lines[idx][match.end():].strip()
|
||||
cmd_rest = [l.strip() for l in lines[idx+1:]]
|
||||
cmd_text = " ".join([cmd_first_line] + cmd_rest).strip()
|
||||
|
||||
if cmd_text:
|
||||
pv = f"{ptxt} {cmd_text}".strip()
|
||||
if len(pv) > 80:
|
||||
pv = pv[:77] + "..."
|
||||
parsed_positions.append({"pos": pos, "type": "VALID_CMD", "preview": pv})
|
||||
else:
|
||||
parsed_positions.append({"pos": pos, "type": "EMPTY_PROMPT", "preview": ""})
|
||||
found_in_pass1 = True
|
||||
break
|
||||
|
||||
if not found_in_pass1:
|
||||
# Fallback: The prompt might have been isolated in the previous chunk
|
||||
# due to asynchronous network delays splitting the output exactly at the newline.
|
||||
prev_was_valid_cmd = i >= 2 and parsed_positions[i-2]["type"] == "VALID_CMD"
|
||||
if prev_pos > 0 and not prev_was_valid_cmd:
|
||||
# Fetch the very last chunk that we just processed
|
||||
prev_prev_pos = cmd_byte_positions[i-2][0] if i >= 2 else 0
|
||||
prev_chunk_text = self._clean_cisco_scrolling(raw_bytes[prev_prev_pos:prev_pos].decode(errors='replace'))
|
||||
prev_lines_text = [l for l in prev_chunk_text.split('\n') if l.strip()]
|
||||
|
||||
if prev_lines_text:
|
||||
prev_match = prompt_re.search(prev_lines_text[-1])
|
||||
if prev_match:
|
||||
ptxt = prev_match.group(0).strip()
|
||||
cmd_text = " ".join([l.strip() for l in lines]).strip()
|
||||
if cmd_text:
|
||||
pv = f"{ptxt} {cmd_text}".strip()
|
||||
if len(pv) > 80:
|
||||
pv = pv[:77] + "..."
|
||||
parsed_positions.append({"pos": pos, "type": "VALID_CMD", "preview": pv})
|
||||
found_in_pass1 = True
|
||||
|
||||
if not found_in_pass1:
|
||||
parsed_positions.append({"pos": pos, "type": "SCROLLING", "preview": ""})
|
||||
else:
|
||||
parsed_positions.append({"pos": pos, "type": "SCROLLING", "preview": ""})
|
||||
|
||||
@@ -468,11 +591,11 @@ el.replaceWith(d);
|
||||
start_pos = item["pos"]
|
||||
preview = item["preview"]
|
||||
|
||||
# Find the end position: next VALID_CMD or EMPTY_PROMPT
|
||||
# Find the end position: next VALID_CMD or EMPTY_PROMPT or CANCELLED
|
||||
end_pos = current_prompt_pos
|
||||
for j in range(i + 1, len(parsed_positions)):
|
||||
next_item = parsed_positions[j]
|
||||
if next_item["type"] in ("VALID_CMD", "EMPTY_PROMPT"):
|
||||
if next_item["type"] in ("VALID_CMD", "EMPTY_PROMPT", "CANCELLED"):
|
||||
end_pos = next_item["pos"]
|
||||
break
|
||||
|
||||
@@ -533,20 +656,22 @@ el.replaceWith(d);
|
||||
<div class="desc"><p>Update MCP server settings in the configuration with smart merging.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.AIService.configure_provider"><code class="name flex">
|
||||
<span>def <span class="ident">configure_provider</span></span>(<span>self, provider, model=None, api_key=None)</span>
|
||||
<span>def <span class="ident">configure_provider</span></span>(<span>self, provider, model=None, api_key=None, auth=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def configure_provider(self, provider, model=None, api_key=None):
|
||||
<pre><code class="python">def configure_provider(self, provider, model=None, api_key=None, auth=None):
|
||||
"""Update AI provider settings in the configuration."""
|
||||
settings = self.config.config.get("ai", {})
|
||||
if model:
|
||||
settings[f"{provider}_model"] = model
|
||||
if api_key:
|
||||
settings[f"{provider}_api_key"] = api_key
|
||||
if auth is not None:
|
||||
settings[f"{provider}_auth"] = auth
|
||||
|
||||
self.config.config["ai"] = settings
|
||||
self.config._saveconfig(self.config.file)</code></pre>
|
||||
@@ -589,21 +714,39 @@ el.replaceWith(d);
|
||||
</details>
|
||||
<div class="desc"><p>Delete an AI session by ID.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.AIService.list_sessions"><code class="name flex">
|
||||
<span>def <span class="ident">list_sessions</span></span>(<span>self)</span>
|
||||
<dt id="connpy.services.AIService.list_mcp_servers"><code class="name flex">
|
||||
<span>def <span class="ident">list_mcp_servers</span></span>(<span>self) ‑> dict</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def list_sessions(self):
|
||||
"""Return a list of all saved AI sessions."""
|
||||
<pre><code class="python">def list_mcp_servers(self) -> dict:
|
||||
"""Get the configured MCP servers."""
|
||||
ai_settings = self.config.config.get("ai", {})
|
||||
return ai_settings.get("mcp_servers", {})</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Get the configured MCP servers.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.AIService.list_sessions"><code class="name flex">
|
||||
<span>def <span class="ident">list_sessions</span></span>(<span>self, limit=None)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def list_sessions(self, limit=None):
|
||||
"""Return a list of saved AI sessions, optionally limited."""
|
||||
from connpy.ai import ai
|
||||
agent = ai(self.config)
|
||||
return agent._get_sessions()</code></pre>
|
||||
sessions = agent._get_sessions()
|
||||
if limit and len(sessions) > limit:
|
||||
return sessions[:limit], len(sessions)
|
||||
return sessions, len(sessions)</code></pre>
|
||||
</details>
|
||||
<div class="desc"><p>Return a list of all saved AI sessions.</p></div>
|
||||
<div class="desc"><p>Return a list of saved AI sessions, optionally limited.</p></div>
|
||||
</dd>
|
||||
<dt id="connpy.services.AIService.load_session_data"><code class="name flex">
|
||||
<span>def <span class="ident">load_session_data</span></span>(<span>self, session_id)</span>
|
||||
@@ -3726,6 +3869,7 @@ el.replaceWith(d);
|
||||
<li><code><a title="connpy.services.AIService.configure_provider" href="#connpy.services.AIService.configure_provider">configure_provider</a></code></li>
|
||||
<li><code><a title="connpy.services.AIService.confirm" href="#connpy.services.AIService.confirm">confirm</a></code></li>
|
||||
<li><code><a title="connpy.services.AIService.delete_session" href="#connpy.services.AIService.delete_session">delete_session</a></code></li>
|
||||
<li><code><a title="connpy.services.AIService.list_mcp_servers" href="#connpy.services.AIService.list_mcp_servers">list_mcp_servers</a></code></li>
|
||||
<li><code><a title="connpy.services.AIService.list_sessions" href="#connpy.services.AIService.list_sessions">list_sessions</a></code></li>
|
||||
<li><code><a title="connpy.services.AIService.load_session_data" href="#connpy.services.AIService.load_session_data">load_session_data</a></code></li>
|
||||
<li><code><a title="connpy.services.AIService.process_copilot_input" href="#connpy.services.AIService.process_copilot_input">process_copilot_input</a></code></li>
|
||||
@@ -3840,7 +3984,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.services.node_service API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -786,7 +786,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.services.plugin_service API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -709,7 +709,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.services.profile_service API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -429,7 +429,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.services.provider API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -164,7 +164,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.services.sync_service API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -964,7 +964,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.services.system_service API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -325,7 +325,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.tunnels API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -545,7 +545,7 @@ Bridges the blocking gRPC iterators with the async _async_interact_loop.</p></di
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta name="generator" content="pdoc3 0.11.5">
|
||||
<meta name="generator" content="pdoc3 0.11.6">
|
||||
<title>connpy.utils API documentation</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
|
||||
@@ -59,11 +59,14 @@ el.replaceWith(d);
|
||||
if not data:
|
||||
return ""
|
||||
|
||||
# Remove OSC (Operating System Command) sequences (e.g., set window title \x1b]0;...\x07)
|
||||
data = re.sub(r'\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)', '', data)
|
||||
|
||||
lines = data.split('\n')
|
||||
cleaned_lines = []
|
||||
|
||||
# Regex to capture: ANSI sequences, control characters (\r, \b, etc), and plain text chunks
|
||||
token_re = re.compile(r'(\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/ ]*[@-~])|\r|\b|\x7f|[\x00-\x1F]|[^\x1B\r\b\x7f\x00-\x1F]+)')
|
||||
token_re = re.compile(r'(\x1B(?:[\x30-\x5A\x5C-\x7E]|\[[0-?]*[ -/ ]*[@-~])|\r|\b|\x7f|[\x00-\x1F]|[^\x1B\r\b\x7f\x00-\x1F]+)')
|
||||
|
||||
for line in lines:
|
||||
buffer = []
|
||||
@@ -144,7 +147,7 @@ el.replaceWith(d);
|
||||
</nav>
|
||||
</main>
|
||||
<footer id="footer">
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
|
||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user