perplexity-web-mcp-cli 0.11.2__tar.gz → 0.12.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/PKG-INFO +2 -2
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/README.md +1 -1
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/pyproject.toml +1 -1
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/cli/ai_doc.py +11 -8
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/cli/main.py +21 -18
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/cli/skill.py +63 -39
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/council.py +9 -13
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/data/SKILL.md +9 -6
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/data/references/mcp-tools.md +1 -1
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/mcp/server.py +32 -21
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/shared.py +85 -20
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/__init__.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/api/__init__.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/api/responses.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/api/server.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/api/session_manager.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/api/tool_calling.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/cli/__init__.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/cli/auth.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/cli/doctor.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/cli/hack.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/cli/setup.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/config.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/constants.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/core.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/data/references/api-endpoints.md +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/data/references/models.md +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/enums.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/exceptions.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/http.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/limits.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/logging.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/mcp/__init__.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/mcp/__main__.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/models.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/py.typed +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/rate_limits.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/resilience.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/router.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/sessions.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/token_store.py +0 -0
- {perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/types.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: perplexity-web-mcp-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.1
|
|
4
4
|
Summary: CLI, MCP server, and Anthropic/OpenAI API-compatible interface for Perplexity AI.
|
|
5
5
|
Keywords: perplexity,ai,mcp,anthropic,api,client
|
|
6
6
|
Author: Jacob BD
|
|
@@ -233,7 +233,7 @@ pwm research "NVIDIA competitive landscape" -s finance --json
|
|
|
233
233
|
Query multiple models in parallel and get a synthesized consensus. Each model costs 1 Pro Search. Default synthesis uses Sonar 2 (also 1 Pro Search).
|
|
234
234
|
|
|
235
235
|
```bash
|
|
236
|
-
# Default: GPT-5.4, Claude
|
|
236
|
+
# Default: GPT-5.4, Claude Sonnet, Gemini Pro + Sonar 2 synthesis (4 Pro Searches)
|
|
237
237
|
pwm council "What are best practices for microservices?"
|
|
238
238
|
```
|
|
239
239
|
|
|
@@ -196,7 +196,7 @@ pwm research "NVIDIA competitive landscape" -s finance --json
|
|
|
196
196
|
Query multiple models in parallel and get a synthesized consensus. Each model costs 1 Pro Search. Default synthesis uses Sonar 2 (also 1 Pro Search).
|
|
197
197
|
|
|
198
198
|
```bash
|
|
199
|
-
# Default: GPT-5.4, Claude
|
|
199
|
+
# Default: GPT-5.4, Claude Sonnet, Gemini Pro + Sonar 2 synthesis (4 Pro Searches)
|
|
200
200
|
pwm council "What are best practices for microservices?"
|
|
201
201
|
```
|
|
202
202
|
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/cli/ai_doc.py
RENAMED
|
@@ -66,7 +66,7 @@ MODEL COUNCIL
|
|
|
66
66
|
pwm council "query" --json Output as JSON
|
|
67
67
|
|
|
68
68
|
Each model in the council costs 1 Pro Search, plus 1 for synthesis. Default = 4 Pro Searches.
|
|
69
|
-
Available models: gpt54, gpt55, claude_sonnet, claude_opus, gemini_pro, nemotron, kimi_k26
|
|
69
|
+
Available models: sonar, gpt54, gpt55, claude_sonnet, claude_opus, gemini_pro, nemotron, kimi_k26
|
|
70
70
|
Thinking toggle: -t / --thinking (gpt54, gpt55, claude_sonnet, claude_opus, kimi_k26 support toggle;
|
|
71
71
|
gemini_pro and nemotron are always thinking)
|
|
72
72
|
|
|
@@ -168,12 +168,13 @@ QUERY TOOLS (each call costs 1 Pro Search query unless noted):
|
|
|
168
168
|
pplx_ask(query, source_focus="web")
|
|
169
169
|
Auto-selects best model. 1 PRO SEARCH per call.
|
|
170
170
|
|
|
171
|
-
pplx_council(query, source_focus="web", models="gpt54,
|
|
171
|
+
pplx_council(query, source_focus="web", models="gpt54,claude_sonnet,gemini_pro",
|
|
172
172
|
synthesize=True, thinking=False, chairman="sonar")
|
|
173
173
|
Model Council — N PRO SEARCHES (1 per model selected).
|
|
174
174
|
BEFORE CALLING: You MUST ask the user which models and how many.
|
|
175
|
-
Available: gpt54, gpt55, claude_sonnet, claude_opus, gemini_pro, nemotron, kimi_k26.
|
|
176
|
-
|
|
175
|
+
Available: sonar, gpt54, gpt55, claude_sonnet, claude_opus, gemini_pro, nemotron, kimi_k26.
|
|
176
|
+
Max-only: gpt55, claude_opus. Exclude these when Subscription is Pro.
|
|
177
|
+
Default: 3 Pro-compatible models (GPT-5.4, Claude Sonnet, Gemini Pro) + synthesis = 4 Pro Searches.
|
|
177
178
|
Synthesis uses Sonar 2 by default. Set chairman to override.
|
|
178
179
|
Non-sonar chairman costs 1 extra Pro Search.
|
|
179
180
|
Set synthesize=False to skip synthesis entirely.
|
|
@@ -200,9 +201,9 @@ QUERY TOOLS (each call costs 1 Pro Search query unless noted):
|
|
|
200
201
|
All query tools accept source_focus: "none", "web", "academic", "social",
|
|
201
202
|
"finance", "all". Use "none" for model-only queries without web search.
|
|
202
203
|
|
|
203
|
-
All query tools also accept an optional `conversation_id` (str) parameter.
|
|
204
|
-
The server returns `[Conversation ID: <uuid>]` at the end of each response.
|
|
205
|
-
Extract this UUID and pass it to the next query to maintain context across
|
|
204
|
+
All query tools also accept an optional `conversation_id` (str) parameter.
|
|
205
|
+
The server returns `[Conversation ID: <uuid>]` at the end of each response.
|
|
206
|
+
Extract this UUID and pass it to the next query to maintain context across
|
|
206
207
|
multiple turns. State is retained in memory for 1 hour.
|
|
207
208
|
|
|
208
209
|
USAGE TOOL (1):
|
|
@@ -287,7 +288,9 @@ MANDATORY PROTOCOL:
|
|
|
287
288
|
3. ESCALATE ONLY WHEN NEEDED: Use 'standard' for multi-source synthesis,
|
|
288
289
|
'detailed' for complex analysis, 'research' only when user requests it.
|
|
289
290
|
4. NEVER USE DEEP RESEARCH AUTONOMOUSLY — always ask the user first.
|
|
290
|
-
5.
|
|
291
|
+
5. SUBSCRIPTION-AWARE MODELS: Read the Subscription line from pplx_usage().
|
|
292
|
+
If it is Pro, exclude Max-only models: gpt55 and claude_opus.
|
|
293
|
+
6. COUNCIL: Before calling pplx_council, ASK the user which models and how
|
|
291
294
|
many. Each model = 1 Pro Search. List available models for them to choose.
|
|
292
295
|
|
|
293
296
|
WHEN TO USE EACH INTENT:
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/cli/main.py
RENAMED
|
@@ -27,14 +27,16 @@ import rich_click as click
|
|
|
27
27
|
|
|
28
28
|
from perplexity_web_mcp.exceptions import AuthenticationError, RateLimitError
|
|
29
29
|
from perplexity_web_mcp.shared import (
|
|
30
|
+
COUNCIL_DEFAULT_MODELS_STR,
|
|
30
31
|
COUNCIL_DISPLAY_NAMES,
|
|
32
|
+
COUNCIL_ELIGIBLE_MODEL_NAMES,
|
|
31
33
|
MODEL_MAP,
|
|
32
34
|
MODEL_NAMES,
|
|
33
35
|
SOURCE_FOCUS_NAMES,
|
|
34
|
-
THINKING_TOGGLEABLE,
|
|
35
36
|
Models,
|
|
36
37
|
SourceFocusName,
|
|
37
38
|
ask,
|
|
39
|
+
build_council_model_list,
|
|
38
40
|
get_limit_cache,
|
|
39
41
|
resolve_model,
|
|
40
42
|
)
|
|
@@ -230,7 +232,7 @@ def _cmd_research_impl(query, source, json_output):
|
|
|
230
232
|
|
|
231
233
|
# ── Council ────────────────────────────────────────────────────────────────
|
|
232
234
|
|
|
233
|
-
COUNCIL_MODEL_NAMES =
|
|
235
|
+
COUNCIL_MODEL_NAMES = COUNCIL_ELIGIBLE_MODEL_NAMES
|
|
234
236
|
|
|
235
237
|
|
|
236
238
|
@cli.command()
|
|
@@ -239,7 +241,7 @@ COUNCIL_MODEL_NAMES = ("gpt54", "gpt55", "claude_sonnet", "claude_opus", "gemini
|
|
|
239
241
|
"-m",
|
|
240
242
|
"--models",
|
|
241
243
|
"models_str",
|
|
242
|
-
default=
|
|
244
|
+
default=COUNCIL_DEFAULT_MODELS_STR,
|
|
243
245
|
help=f"Comma-separated models ({', '.join(COUNCIL_MODEL_NAMES)}).",
|
|
244
246
|
)
|
|
245
247
|
@click.option("-t", "--thinking", is_flag=True, help="Enable extended thinking mode.")
|
|
@@ -301,14 +303,8 @@ def _cmd_council_impl(query, models_str, source, synthesize, json_output, thinki
|
|
|
301
303
|
|
|
302
304
|
# Build model list (None = use defaults)
|
|
303
305
|
model_list = None
|
|
304
|
-
if models_str !=
|
|
305
|
-
model_list =
|
|
306
|
-
for name in model_names:
|
|
307
|
-
resolved = resolve_model(name, thinking=thinking)
|
|
308
|
-
display = COUNCIL_DISPLAY_NAMES.get(name, name)
|
|
309
|
-
if thinking and name in THINKING_TOGGLEABLE:
|
|
310
|
-
display += " Thinking"
|
|
311
|
-
model_list.append((display, resolved))
|
|
306
|
+
if models_str != COUNCIL_DEFAULT_MODELS_STR:
|
|
307
|
+
model_list = build_council_model_list(model_names, thinking=thinking)
|
|
312
308
|
|
|
313
309
|
synthesis_model = resolve_model(chairman) if chairman != "sonar" else None
|
|
314
310
|
|
|
@@ -454,16 +450,23 @@ def _cmd_usage_impl(refresh):
|
|
|
454
450
|
|
|
455
451
|
# ── Account Info ───────────────────────────────────────────────────────
|
|
456
452
|
settings = cache.get_user_settings(force_refresh=refresh)
|
|
457
|
-
|
|
453
|
+
from perplexity_web_mcp.cli.auth import get_user_info
|
|
454
|
+
|
|
455
|
+
user_info = get_user_info(token)
|
|
456
|
+
if settings or user_info:
|
|
458
457
|
table = Table(title="👤 Account", show_header=True, header_style="bold cyan")
|
|
459
458
|
table.add_column("Field", style="bold")
|
|
460
459
|
table.add_column("Value", justify="right")
|
|
461
460
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
461
|
+
if user_info:
|
|
462
|
+
table.add_row("Subscription", f"[bold]{user_info.tier_display}[/]")
|
|
463
|
+
|
|
464
|
+
if settings:
|
|
465
|
+
billing = settings.subscription_tier or "unknown"
|
|
466
|
+
status = settings.subscription_status
|
|
467
|
+
table.add_row("Billing", f"[bold]{billing}[/] ({status})")
|
|
468
|
+
table.add_row("Total Queries", f"{settings.query_count:,}")
|
|
469
|
+
table.add_row("Pro Queries", f"{settings.query_count_copilot:,}")
|
|
467
470
|
|
|
468
471
|
console.print(table)
|
|
469
472
|
|
|
@@ -732,7 +735,7 @@ def _cmd_council(args: list[str]) -> int:
|
|
|
732
735
|
return 1
|
|
733
736
|
|
|
734
737
|
query = args[0]
|
|
735
|
-
models_str =
|
|
738
|
+
models_str = COUNCIL_DEFAULT_MODELS_STR
|
|
736
739
|
source: SourceFocusName = "web"
|
|
737
740
|
synthesize = True
|
|
738
741
|
json_output = False
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/cli/skill.py
RENAMED
|
@@ -6,8 +6,9 @@ to the appropriate location for each supported AI platform.
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
-
from dataclasses import dataclass
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
10
|
from importlib import metadata
|
|
11
|
+
import os
|
|
11
12
|
from pathlib import Path
|
|
12
13
|
import re
|
|
13
14
|
import shutil
|
|
@@ -17,6 +18,11 @@ import sys
|
|
|
17
18
|
SKILL_DIR_NAME = "perplexity-web-mcp"
|
|
18
19
|
|
|
19
20
|
|
|
21
|
+
def _hermes_home() -> Path:
|
|
22
|
+
"""Resolve the Hermes root directory, respecting $HERMES_HOME."""
|
|
23
|
+
return Path(os.environ.get("HERMES_HOME", Path.home() / ".hermes"))
|
|
24
|
+
|
|
25
|
+
|
|
20
26
|
@dataclass(frozen=True)
|
|
21
27
|
class SkillTarget:
|
|
22
28
|
"""A platform that supports Agent Skills."""
|
|
@@ -25,72 +31,95 @@ class SkillTarget:
|
|
|
25
31
|
description: str
|
|
26
32
|
user_dir: Path
|
|
27
33
|
project_dir: str
|
|
34
|
+
binary: str | None = None
|
|
35
|
+
root_dirs: list[Path] = field(default_factory=list)
|
|
28
36
|
frontmatter_extras: dict[str, str] | None = None
|
|
29
37
|
|
|
30
38
|
|
|
31
|
-
def _home() -> Path:
|
|
32
|
-
return Path.home()
|
|
33
|
-
|
|
34
|
-
|
|
35
39
|
def _get_targets() -> list[SkillTarget]:
|
|
36
40
|
"""Return the list of platforms that support skills."""
|
|
37
|
-
home =
|
|
41
|
+
home = Path.home()
|
|
42
|
+
hm_root = _hermes_home()
|
|
38
43
|
return [
|
|
39
44
|
SkillTarget(
|
|
40
45
|
name="claude-code",
|
|
41
46
|
description="Claude Code CLI and Desktop",
|
|
42
47
|
user_dir=home / ".claude" / "skills",
|
|
43
48
|
project_dir=".claude/skills",
|
|
49
|
+
binary="claude",
|
|
50
|
+
root_dirs=[home / ".claude"],
|
|
44
51
|
),
|
|
45
52
|
SkillTarget(
|
|
46
53
|
name="cursor",
|
|
47
54
|
description="Cursor AI editor",
|
|
48
55
|
user_dir=home / ".cursor" / "skills",
|
|
49
56
|
project_dir=".cursor/skills",
|
|
57
|
+
binary="cursor",
|
|
58
|
+
root_dirs=[home / ".cursor"],
|
|
50
59
|
),
|
|
51
60
|
SkillTarget(
|
|
52
61
|
name="codex",
|
|
53
62
|
description="OpenAI Codex CLI",
|
|
54
63
|
user_dir=home / ".agents" / "skills",
|
|
55
64
|
project_dir=".agents/skills",
|
|
65
|
+
binary="codex",
|
|
66
|
+
root_dirs=[home / ".codex", home / ".agents"],
|
|
56
67
|
),
|
|
57
68
|
SkillTarget(
|
|
58
69
|
name="opencode",
|
|
59
70
|
description="OpenCode AI assistant",
|
|
60
71
|
user_dir=home / ".config" / "opencode" / "skills",
|
|
61
72
|
project_dir=".opencode/skills",
|
|
73
|
+
binary="opencode",
|
|
74
|
+
root_dirs=[home / ".config" / "opencode"],
|
|
62
75
|
),
|
|
63
76
|
SkillTarget(
|
|
64
77
|
name="gemini-cli",
|
|
65
78
|
description="Google Gemini CLI",
|
|
66
79
|
user_dir=home / ".agents" / "skills",
|
|
67
80
|
project_dir=".agents/skills",
|
|
81
|
+
binary="gemini",
|
|
82
|
+
root_dirs=[home / ".agents", home / ".gemini"],
|
|
68
83
|
),
|
|
69
84
|
SkillTarget(
|
|
70
85
|
name="antigravity",
|
|
71
86
|
description="Google Antigravity IDE",
|
|
72
87
|
user_dir=home / ".gemini" / "antigravity" / "skills",
|
|
73
88
|
project_dir=".agent/skills",
|
|
89
|
+
root_dirs=[home / ".gemini" / "antigravity"],
|
|
74
90
|
),
|
|
75
91
|
SkillTarget(
|
|
76
92
|
name="cline",
|
|
77
93
|
description="Cline CLI terminal agent",
|
|
78
94
|
user_dir=home / ".cline" / "skills",
|
|
79
95
|
project_dir=".cline/skills",
|
|
96
|
+
binary="cline",
|
|
97
|
+
root_dirs=[home / ".cline"],
|
|
80
98
|
),
|
|
81
99
|
SkillTarget(
|
|
82
100
|
name="openclaw",
|
|
83
101
|
description="OpenClaw AI agent framework",
|
|
84
102
|
user_dir=home / ".openclaw" / "workspace" / "skills",
|
|
85
103
|
project_dir=".openclaw/workspace/skills",
|
|
104
|
+
binary="openclaw",
|
|
105
|
+
root_dirs=[home / ".openclaw"],
|
|
86
106
|
),
|
|
87
107
|
SkillTarget(
|
|
88
108
|
name="alef-agent",
|
|
89
109
|
description="Alef Agent AI framework",
|
|
90
110
|
user_dir=home / ".alef-agent" / "workspace" / "skills",
|
|
91
111
|
project_dir=".alef-agent/workspace/skills",
|
|
112
|
+
root_dirs=[home / ".alef-agent"],
|
|
92
113
|
frontmatter_extras={"type": "tool", "status": "approved"},
|
|
93
114
|
),
|
|
115
|
+
SkillTarget(
|
|
116
|
+
name="hermes",
|
|
117
|
+
description="Hermes Agent (NousResearch)",
|
|
118
|
+
user_dir=hm_root / "skills",
|
|
119
|
+
project_dir=".hermes/skills",
|
|
120
|
+
binary="hermes",
|
|
121
|
+
root_dirs=[hm_root],
|
|
122
|
+
),
|
|
94
123
|
SkillTarget(
|
|
95
124
|
name="other",
|
|
96
125
|
description="Export all formats for manual install",
|
|
@@ -100,33 +129,17 @@ def _get_targets() -> list[SkillTarget]:
|
|
|
100
129
|
]
|
|
101
130
|
|
|
102
131
|
|
|
103
|
-
def
|
|
104
|
-
"""
|
|
105
|
-
|
|
106
|
-
Looks for the tool's config directory (parent of its skills dir) and
|
|
107
|
-
verifies the tool itself created content there -- not just our own
|
|
108
|
-
``skills/`` subdirectory from a previous install.
|
|
132
|
+
def _is_tool_installed(target: SkillTarget) -> bool:
|
|
133
|
+
"""Detect whether a tool is actually installed on this system.
|
|
109
134
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
135
|
+
Checks two signals (either is sufficient):
|
|
136
|
+
1. Binary on PATH (e.g. ``claude``, ``cursor``, ``opencode``, ``hermes``)
|
|
137
|
+
2. Tool's root config directory exists (e.g. ``~/.claude``, ``~/.cursor``)
|
|
113
138
|
"""
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return shutil.which("gemini") is not None
|
|
119
|
-
|
|
120
|
-
config_root = target.user_dir.parent
|
|
121
|
-
if not config_root.is_dir():
|
|
122
|
-
return False
|
|
123
|
-
try:
|
|
124
|
-
for child in config_root.iterdir():
|
|
125
|
-
if child.name != "skills":
|
|
126
|
-
return True
|
|
127
|
-
except OSError:
|
|
128
|
-
return False
|
|
129
|
-
return False
|
|
139
|
+
if target.binary and shutil.which(target.binary):
|
|
140
|
+
return True
|
|
141
|
+
|
|
142
|
+
return any(root_dir.is_dir() for root_dir in target.root_dirs)
|
|
130
143
|
|
|
131
144
|
|
|
132
145
|
def _find_skill_source() -> Path | None:
|
|
@@ -163,10 +176,16 @@ def _get_installed_version(target_dir: Path) -> str | None:
|
|
|
163
176
|
return None
|
|
164
177
|
try:
|
|
165
178
|
text = skill_file.read_text(encoding="utf-8")
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
179
|
+
in_frontmatter = False
|
|
180
|
+
for raw_line in text.split("\n"):
|
|
181
|
+
stripped = raw_line.strip()
|
|
182
|
+
if stripped == "---":
|
|
183
|
+
if not in_frontmatter:
|
|
184
|
+
in_frontmatter = True
|
|
185
|
+
continue
|
|
186
|
+
break # closing --- reached
|
|
187
|
+
if stripped.startswith("version:"):
|
|
188
|
+
return stripped.split(":", 1)[1].strip().strip('"').strip("'")
|
|
170
189
|
except OSError:
|
|
171
190
|
pass
|
|
172
191
|
return None
|
|
@@ -304,6 +323,11 @@ cp -r {SKILL_DIR_NAME} ~/.openclaw/workspace/skills/
|
|
|
304
323
|
cp -r {SKILL_DIR_NAME} ~/.alef-agent/workspace/skills/
|
|
305
324
|
```
|
|
306
325
|
|
|
326
|
+
### Hermes Agent
|
|
327
|
+
```bash
|
|
328
|
+
cp -r {SKILL_DIR_NAME} ~/.hermes/skills/
|
|
329
|
+
```
|
|
330
|
+
|
|
307
331
|
## Automated Installation
|
|
308
332
|
|
|
309
333
|
Instead of manual copying, you can use:
|
|
@@ -311,7 +335,7 @@ Instead of manual copying, you can use:
|
|
|
311
335
|
pwm skill install <tool>
|
|
312
336
|
```
|
|
313
337
|
|
|
314
|
-
Where `<tool>` is: claude-code, cursor, codex, opencode, gemini-cli, antigravity, cline, openclaw, alef-agent.
|
|
338
|
+
Where `<tool>` is: claude-code, cursor, codex, opencode, gemini-cli, antigravity, cline, openclaw, alef-agent, hermes.
|
|
315
339
|
"""
|
|
316
340
|
|
|
317
341
|
(export_dir / "README.md").write_text(readme_content)
|
|
@@ -336,14 +360,14 @@ def _install_all(targets: list[SkillTarget], current_version: str) -> int:
|
|
|
336
360
|
for t in targets:
|
|
337
361
|
if t.name == "other":
|
|
338
362
|
continue
|
|
339
|
-
if
|
|
363
|
+
if _is_tool_installed(t):
|
|
340
364
|
detected.append(t)
|
|
341
365
|
else:
|
|
342
366
|
not_detected.append(t.name)
|
|
343
367
|
|
|
344
368
|
if not detected:
|
|
345
369
|
print(" No supported tools detected on this system.")
|
|
346
|
-
print(
|
|
370
|
+
print(" (No binary on PATH and no config directory found for any tool)")
|
|
347
371
|
return 0
|
|
348
372
|
|
|
349
373
|
installed: list[str] = []
|
|
@@ -405,7 +429,7 @@ def cmd_skill(args: list[str]) -> int:
|
|
|
405
429
|
" pwm skill show Display the skill content\n"
|
|
406
430
|
" pwm skill update Update all outdated skills\n"
|
|
407
431
|
"\n"
|
|
408
|
-
"Tools: claude-code, cursor, codex, opencode, gemini-cli, antigravity, cline, openclaw, alef-agent, other, all\n"
|
|
432
|
+
"Tools: claude-code, cursor, codex, opencode, gemini-cli, antigravity, cline, openclaw, alef-agent, hermes, other, all\n"
|
|
409
433
|
"\n"
|
|
410
434
|
"Examples:\n"
|
|
411
435
|
" pwm skill list\n"
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/council.py
RENAMED
|
@@ -16,6 +16,7 @@ from .config import ConversationConfig
|
|
|
16
16
|
from .enums import CitationMode, SearchFocus, SourceFocus
|
|
17
17
|
from .logging import get_logger
|
|
18
18
|
from .models import Model, Models
|
|
19
|
+
from .shared import COUNCIL_DEFAULT_MODEL_NAMES, build_council_model_list
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
if TYPE_CHECKING:
|
|
@@ -29,19 +30,14 @@ logger = get_logger(__name__)
|
|
|
29
30
|
# Default council composition
|
|
30
31
|
# ---------------------------------------------------------------------------
|
|
31
32
|
|
|
32
|
-
COUNCIL_DEFAULT_MODELS: list[tuple[str, Model]] =
|
|
33
|
-
|
|
34
|
-
("Claude Opus 4.7", Models.CLAUDE_47_OPUS),
|
|
35
|
-
("Gemini 3.1 Pro", Models.GEMINI_31_PRO_THINKING),
|
|
36
|
-
]
|
|
37
|
-
"""Default models for the council (3 diverse providers)."""
|
|
33
|
+
COUNCIL_DEFAULT_MODELS: list[tuple[str, Model]] = build_council_model_list(COUNCIL_DEFAULT_MODEL_NAMES)
|
|
34
|
+
"""Default Pro-compatible models for the council (3 diverse providers)."""
|
|
38
35
|
|
|
39
|
-
COUNCIL_DEFAULT_MODELS_THINKING: list[tuple[str, Model]] =
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"""Default models for the council with extended thinking enabled."""
|
|
36
|
+
COUNCIL_DEFAULT_MODELS_THINKING: list[tuple[str, Model]] = build_council_model_list(
|
|
37
|
+
COUNCIL_DEFAULT_MODEL_NAMES,
|
|
38
|
+
thinking=True,
|
|
39
|
+
)
|
|
40
|
+
"""Default Pro-compatible models for the council with extended thinking enabled."""
|
|
45
41
|
|
|
46
42
|
|
|
47
43
|
# ---------------------------------------------------------------------------
|
|
@@ -220,7 +216,7 @@ def council_ask(
|
|
|
220
216
|
Args:
|
|
221
217
|
query: The question to ask all models.
|
|
222
218
|
models: List of (display_name, Model) tuples. Defaults to
|
|
223
|
-
COUNCIL_DEFAULT_MODELS (GPT-5.4, Claude
|
|
219
|
+
COUNCIL_DEFAULT_MODELS (GPT-5.4, Claude Sonnet, Gemini Pro).
|
|
224
220
|
source_focus: Source focus for all queries (none/web/academic/social/finance/all).
|
|
225
221
|
synthesize: Whether to produce a synthesized consensus (adds 1 Sonar 2 synthesis query by default).
|
|
226
222
|
thinking: Use thinking model variants for default council members.
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/data/SKILL.md
RENAMED
|
@@ -46,8 +46,9 @@ the weekly pool fast, leaving nothing for questions that actually need it.
|
|
|
46
46
|
### Before Every Session
|
|
47
47
|
|
|
48
48
|
1. **Check quota first**: Call `pplx_usage()` (MCP) or `pwm usage` (CLI) before your first query.
|
|
49
|
-
2. Review the remaining Pro and Research counts.
|
|
50
|
-
3. If
|
|
49
|
+
2. Review the remaining Pro and Research counts and the `Subscription` line.
|
|
50
|
+
3. If Subscription is Pro, exclude Max-only models (`gpt55`, `claude_opus`) from model selection and councils.
|
|
51
|
+
4. If Pro < 20% remaining, restrict yourself to quick/Sonar 2 for everything except user-requested Pro queries.
|
|
51
52
|
|
|
52
53
|
### Before Every Query: Choose the Lowest Sufficient Tier
|
|
53
54
|
|
|
@@ -83,8 +84,9 @@ Ask yourself: **"Can Sonar 2 answer this?"** If yes, use `quick`. Only escalate
|
|
|
83
84
|
- The user needs high-confidence answers validated across multiple AI providers
|
|
84
85
|
- Important decisions, fact-checking, or complex analysis
|
|
85
86
|
- BEFORE calling: ASK the user which models and how many (each = 1 Pro Search)
|
|
86
|
-
- Available models: gpt54, gpt55, claude_sonnet, claude_opus, gemini_pro, nemotron, kimi_k26
|
|
87
|
-
-
|
|
87
|
+
- Available models: sonar, gpt54, gpt55, claude_sonnet, claude_opus, gemini_pro, nemotron, kimi_k26
|
|
88
|
+
- Max-only models: gpt55, claude_opus. Do not use these for Pro subscriptions.
|
|
89
|
+
- Default: 3 Pro-compatible models (GPT-5.4, Claude Sonnet, Gemini Pro) + synthesis = 4 Pro Searches
|
|
88
90
|
|
|
89
91
|
### Decision Flowchart
|
|
90
92
|
|
|
@@ -237,7 +239,8 @@ pwm ask "protein folding advances" -m gemini_pro -s academic --json
|
|
|
237
239
|
### Model Council
|
|
238
240
|
|
|
239
241
|
Query multiple models in parallel and get a synthesized consensus.
|
|
240
|
-
Each model in the council costs 1 Pro Search, plus 1 for Sonar 2 synthesis. Default: 3 models + synthesis = 4 Pro Searches.
|
|
242
|
+
Each model in the council costs 1 Pro Search, plus 1 for Sonar 2 synthesis. Default: 3 Pro-compatible models + synthesis = 4 Pro Searches.
|
|
243
|
+
Before selecting models, check `pplx_usage()` or `pwm usage`. If the subscription is Pro, exclude Max-only models (`gpt55`, `claude_opus`).
|
|
241
244
|
|
|
242
245
|
```bash
|
|
243
246
|
pwm council "What are the best practices for microservices?" # default 3 models
|
|
@@ -283,7 +286,7 @@ pwm usage --refresh # Force-refresh from server
|
|
|
283
286
|
| `pplx_sonar` | 1 Pro Search | Perplexity Sonar 2 |
|
|
284
287
|
| `pplx_query` | 1 Pro | Explicit model selection with thinking toggle |
|
|
285
288
|
| `pplx_ask` | 1 Pro | Quick Q&A (auto model) |
|
|
286
|
-
| `pplx_council` | **N+1 Pro** (1 per model + 1 synthesis) | Model Council — **ASK USER which models first!** Supports `thinking=True` and `chairman` for synthesis model. |
|
|
289
|
+
| `pplx_council` | **N+1 Pro** (1 per model + 1 synthesis) | Model Council — **ASK USER which models first!** Check subscription first; exclude Max-only `gpt55`/`claude_opus` on Pro. Supports `thinking=True` and `chairman` for synthesis model. |
|
|
287
290
|
| `pplx_gpt54` / `_thinking` | 1 Pro | OpenAI GPT-5.4 (versatile) |
|
|
288
291
|
| `pplx_gpt55` / `_thinking` | 1 Pro | OpenAI GPT-5.5 (latest, Max tier) |
|
|
289
292
|
| `pplx_claude_sonnet` / `_think` | 1 Pro | Anthropic Claude 4.6 Sonnet |
|
|
@@ -120,7 +120,7 @@ Returns a summary including:
|
|
|
120
120
|
- Deep Research remaining (monthly)
|
|
121
121
|
- Create Files & Apps remaining (monthly)
|
|
122
122
|
- Browser Agent remaining (monthly)
|
|
123
|
-
- Subscription tier and account info
|
|
123
|
+
- Subscription tier, billing detail, and account info
|
|
124
124
|
|
|
125
125
|
## Authentication Tools
|
|
126
126
|
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/mcp/server.py
RENAMED
|
@@ -15,11 +15,11 @@ from fastmcp import FastMCP
|
|
|
15
15
|
|
|
16
16
|
from perplexity_web_mcp.models import Models
|
|
17
17
|
from perplexity_web_mcp.shared import (
|
|
18
|
-
|
|
19
|
-
THINKING_TOGGLEABLE,
|
|
18
|
+
COUNCIL_DEFAULT_MODELS_STR,
|
|
20
19
|
ModelName,
|
|
21
20
|
SourceFocusName,
|
|
22
21
|
ask,
|
|
22
|
+
build_council_model_list,
|
|
23
23
|
council_ask,
|
|
24
24
|
get_limit_cache,
|
|
25
25
|
resolve_model,
|
|
@@ -39,6 +39,7 @@ mcp = FastMCP(
|
|
|
39
39
|
"- pplx_deep_research: 1 DEEP RESEARCH each (small monthly pool, ~5-10 total)\n\n"
|
|
40
40
|
"MANDATORY PROTOCOL:\n"
|
|
41
41
|
"1. On your FIRST query of the session, call pplx_usage() to check remaining quotas.\n"
|
|
42
|
+
" Read the Subscription line: Pro users must avoid Max-only models.\n"
|
|
42
43
|
"2. DEFAULT to pplx_smart_query(intent='quick') for most lookups — it prefers Sonar 2 "
|
|
43
44
|
"before premium models when that fits the question.\n"
|
|
44
45
|
"3. Only use 'standard' or 'detailed' intent when the question requires synthesis, "
|
|
@@ -288,7 +289,7 @@ def pplx_smart_query(
|
|
|
288
289
|
def pplx_council(
|
|
289
290
|
query: str,
|
|
290
291
|
source_focus: SourceFocusName = "web",
|
|
291
|
-
models: str =
|
|
292
|
+
models: str = COUNCIL_DEFAULT_MODELS_STR,
|
|
292
293
|
synthesize: bool = True,
|
|
293
294
|
thinking: bool = False,
|
|
294
295
|
chairman: ModelName = "sonar",
|
|
@@ -296,20 +297,22 @@ def pplx_council(
|
|
|
296
297
|
"""Model Council — query multiple models in parallel, get synthesized consensus.
|
|
297
298
|
|
|
298
299
|
IMPORTANT — BEFORE calling this tool, you MUST:
|
|
299
|
-
1. Tell the user the available models: gpt54, gpt55, claude_sonnet, claude_opus, gemini_pro, nemotron, kimi_k26
|
|
300
|
-
2.
|
|
301
|
-
3.
|
|
300
|
+
1. Tell the user the available models: sonar, gpt54, gpt55, claude_sonnet, claude_opus, gemini_pro, nemotron, kimi_k26
|
|
301
|
+
2. Check pplx_usage() first. If Subscription is Pro, do not include Max-only models: gpt55, claude_opus
|
|
302
|
+
3. Ask the user WHICH models they want in their council and HOW MANY
|
|
303
|
+
4. Inform them of the cost: each council model = 1 Pro Search query, plus synthesis
|
|
302
304
|
(default chairman sonar = Sonar 2 pass — still counts as a normal query toward limits)
|
|
303
|
-
|
|
305
|
+
5. Get explicit confirmation before executing
|
|
304
306
|
|
|
305
|
-
Default council: GPT-5.4, Claude
|
|
307
|
+
Default council: GPT-5.4, Claude Sonnet 4.6, Gemini 3.1 Pro (Pro-compatible, 3 diverse providers).
|
|
306
308
|
|
|
307
309
|
Args:
|
|
308
310
|
query: The question to ask all council models
|
|
309
311
|
source_focus: Source type for all models (none/web/academic/social/finance/all)
|
|
310
312
|
models: Comma-separated model names to use as council members.
|
|
311
|
-
Available: gpt54, gpt55, claude_sonnet, claude_opus, gemini_pro, nemotron, kimi_k26.
|
|
312
|
-
Default: "gpt54,
|
|
313
|
+
Available: sonar, gpt54, gpt55, claude_sonnet, claude_opus, gemini_pro, nemotron, kimi_k26.
|
|
314
|
+
Default: "gpt54,claude_sonnet,gemini_pro" (3 models + synthesis = 4 Pro Searches)
|
|
315
|
+
Max-only: gpt55, claude_opus. Exclude these when pplx_usage shows a Pro subscription.
|
|
313
316
|
synthesize: Whether to synthesize a consensus from all responses.
|
|
314
317
|
Set false to get only individual responses (saves 1 Sonar 2 call).
|
|
315
318
|
thinking: Enable extended thinking for council models (gpt54, gpt55, claude_sonnet,
|
|
@@ -319,15 +322,9 @@ def pplx_council(
|
|
|
319
322
|
"""
|
|
320
323
|
# Parse custom model list if provided
|
|
321
324
|
model_list = None
|
|
322
|
-
if models !=
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
name = name.strip()
|
|
326
|
-
resolved = resolve_model(name, thinking=thinking)
|
|
327
|
-
display = COUNCIL_DISPLAY_NAMES.get(name, name)
|
|
328
|
-
if thinking and name in THINKING_TOGGLEABLE:
|
|
329
|
-
display += " Thinking"
|
|
330
|
-
model_list.append((display, resolved))
|
|
325
|
+
if models != COUNCIL_DEFAULT_MODELS_STR:
|
|
326
|
+
model_names = [name.strip() for name in models.split(",") if name.strip()]
|
|
327
|
+
model_list = build_council_model_list(model_names, thinking=thinking)
|
|
331
328
|
|
|
332
329
|
synthesis_model = resolve_model(chairman) if chairman != "sonar" else None
|
|
333
330
|
|
|
@@ -376,12 +373,26 @@ def pplx_usage(refresh: bool = False) -> str:
|
|
|
376
373
|
else:
|
|
377
374
|
parts.append("WARNING: Could not fetch rate limits (network error or token issue).")
|
|
378
375
|
|
|
376
|
+
from perplexity_web_mcp.cli.auth import get_user_info
|
|
377
|
+
|
|
378
|
+
user_info = get_user_info(token)
|
|
379
379
|
settings = cache.get_user_settings(force_refresh=refresh)
|
|
380
|
-
if settings:
|
|
380
|
+
if settings or user_info:
|
|
381
381
|
parts.append("")
|
|
382
382
|
parts.append("ACCOUNT INFO")
|
|
383
383
|
parts.append("=" * 40)
|
|
384
|
-
|
|
384
|
+
if user_info:
|
|
385
|
+
parts.append(f"Subscription: {user_info.tier_display}")
|
|
386
|
+
if settings:
|
|
387
|
+
parts.append(f"Billing: {settings.subscription_tier} ({settings.subscription_status})")
|
|
388
|
+
parts.append(f"Total queries: {settings.query_count:,}")
|
|
389
|
+
parts.append(f"Pro queries: {settings.query_count_copilot:,}")
|
|
390
|
+
parts.append(f"Upload limit: {settings.upload_limit} files")
|
|
391
|
+
parts.append(f"Create limit: {settings.create_limit}")
|
|
392
|
+
parts.append(f"Pages limit: {settings.pages_limit}")
|
|
393
|
+
parts.append(f"Max files/user: {settings.max_files_per_user:,}")
|
|
394
|
+
parts.append(f"Max file size: {settings.connector_limits.max_file_size_mb} MB")
|
|
395
|
+
parts.append(f"Daily attachments: {settings.connector_limits.daily_attachment_limit}")
|
|
385
396
|
|
|
386
397
|
credits = cache.get_credits(force_refresh=refresh)
|
|
387
398
|
if credits:
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/shared.py
RENAMED
|
@@ -7,6 +7,7 @@ Both the MCP server (mcp/server.py) and CLI (cli/main.py) import from here.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
from dataclasses import dataclass
|
|
10
11
|
from threading import Lock
|
|
11
12
|
from typing import TYPE_CHECKING, Literal
|
|
12
13
|
from uuid import uuid4
|
|
@@ -22,6 +23,7 @@ from .token_store import get_token_or_raise, load_token
|
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
if TYPE_CHECKING:
|
|
26
|
+
from .council import CouncilResponse
|
|
25
27
|
from .types import SearchResultItem
|
|
26
28
|
|
|
27
29
|
|
|
@@ -29,6 +31,20 @@ if TYPE_CHECKING:
|
|
|
29
31
|
# Model and source focus mappings (single source of truth)
|
|
30
32
|
# ---------------------------------------------------------------------------
|
|
31
33
|
|
|
34
|
+
SubscriptionMinimumTier = Literal["free", "pro", "max"]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(frozen=True, slots=True)
|
|
38
|
+
class ModelDefinition:
|
|
39
|
+
"""Metadata and model instances for one user-facing model key."""
|
|
40
|
+
|
|
41
|
+
base_model: Model
|
|
42
|
+
thinking_model: Model | None
|
|
43
|
+
display_name: str
|
|
44
|
+
provider: str
|
|
45
|
+
minimum_tier: SubscriptionMinimumTier = "pro"
|
|
46
|
+
council_eligible: bool = True
|
|
47
|
+
|
|
32
48
|
SOURCE_FOCUS_MAP: dict[str, list[SourceFocus]] = {
|
|
33
49
|
"none": [],
|
|
34
50
|
"web": [SourceFocus.WEB],
|
|
@@ -38,18 +54,49 @@ SOURCE_FOCUS_MAP: dict[str, list[SourceFocus]] = {
|
|
|
38
54
|
"all": [SourceFocus.WEB, SourceFocus.ACADEMIC, SourceFocus.SOCIAL],
|
|
39
55
|
}
|
|
40
56
|
|
|
57
|
+
MODEL_METADATA: dict[str, ModelDefinition] = {
|
|
58
|
+
"auto": ModelDefinition(Models.BEST, None, "Auto (Best)", "Perplexity", council_eligible=False),
|
|
59
|
+
"sonar": ModelDefinition(Models.SONAR, None, "Sonar 2", "Perplexity"),
|
|
60
|
+
"deep_research": ModelDefinition(
|
|
61
|
+
Models.DEEP_RESEARCH,
|
|
62
|
+
None,
|
|
63
|
+
"Deep Research",
|
|
64
|
+
"Perplexity",
|
|
65
|
+
council_eligible=False,
|
|
66
|
+
),
|
|
67
|
+
"gpt54": ModelDefinition(Models.GPT_54, Models.GPT_54_THINKING, "GPT-5.4", "OpenAI"),
|
|
68
|
+
"gpt55": ModelDefinition(Models.GPT_55, Models.GPT_55_THINKING, "GPT-5.5", "OpenAI", minimum_tier="max"),
|
|
69
|
+
"claude_sonnet": ModelDefinition(
|
|
70
|
+
Models.CLAUDE_46_SONNET,
|
|
71
|
+
Models.CLAUDE_46_SONNET_THINKING,
|
|
72
|
+
"Claude Sonnet 4.6",
|
|
73
|
+
"Anthropic",
|
|
74
|
+
),
|
|
75
|
+
"claude_opus": ModelDefinition(
|
|
76
|
+
Models.CLAUDE_47_OPUS,
|
|
77
|
+
Models.CLAUDE_47_OPUS_THINKING,
|
|
78
|
+
"Claude Opus 4.7",
|
|
79
|
+
"Anthropic",
|
|
80
|
+
minimum_tier="max",
|
|
81
|
+
),
|
|
82
|
+
"gemini_pro": ModelDefinition(
|
|
83
|
+
Models.GEMINI_31_PRO_THINKING,
|
|
84
|
+
Models.GEMINI_31_PRO_THINKING,
|
|
85
|
+
"Gemini 3.1 Pro",
|
|
86
|
+
"Google",
|
|
87
|
+
),
|
|
88
|
+
"nemotron": ModelDefinition(
|
|
89
|
+
Models.NEMOTRON_3_SUPER,
|
|
90
|
+
Models.NEMOTRON_3_SUPER,
|
|
91
|
+
"Nemotron 3 Super",
|
|
92
|
+
"NVIDIA",
|
|
93
|
+
),
|
|
94
|
+
"kimi_k26": ModelDefinition(Models.KIMI_K2_6, Models.KIMI_K2_6_THINKING, "Kimi K2.6", "Moonshot"),
|
|
95
|
+
}
|
|
96
|
+
"""User-facing model metadata. Update this table when model names or tier availability changes."""
|
|
97
|
+
|
|
41
98
|
MODEL_MAP: dict[str, tuple[Model, Model | None]] = {
|
|
42
|
-
|
|
43
|
-
"auto": (Models.BEST, None),
|
|
44
|
-
"sonar": (Models.SONAR, None),
|
|
45
|
-
"deep_research": (Models.DEEP_RESEARCH, None),
|
|
46
|
-
"gpt54": (Models.GPT_54, Models.GPT_54_THINKING),
|
|
47
|
-
"gpt55": (Models.GPT_55, Models.GPT_55_THINKING),
|
|
48
|
-
"claude_sonnet": (Models.CLAUDE_46_SONNET, Models.CLAUDE_46_SONNET_THINKING),
|
|
49
|
-
"claude_opus": (Models.CLAUDE_47_OPUS, Models.CLAUDE_47_OPUS_THINKING),
|
|
50
|
-
"gemini_pro": (Models.GEMINI_31_PRO_THINKING, Models.GEMINI_31_PRO_THINKING),
|
|
51
|
-
"nemotron": (Models.NEMOTRON_3_SUPER, Models.NEMOTRON_3_SUPER),
|
|
52
|
-
"kimi_k26": (Models.KIMI_K2_6, Models.KIMI_K2_6_THINKING),
|
|
99
|
+
name: (definition.base_model, definition.thinking_model) for name, definition in MODEL_METADATA.items()
|
|
53
100
|
}
|
|
54
101
|
|
|
55
102
|
SourceFocusName = Literal["none", "web", "academic", "social", "finance", "all"]
|
|
@@ -70,21 +117,39 @@ MODEL_NAMES: list[str] = list(MODEL_MAP.keys())
|
|
|
70
117
|
SOURCE_FOCUS_NAMES: list[str] = list(SOURCE_FOCUS_MAP.keys())
|
|
71
118
|
|
|
72
119
|
COUNCIL_DISPLAY_NAMES: dict[str, str] = {
|
|
73
|
-
|
|
74
|
-
"sonar": "Sonar 2",
|
|
75
|
-
"gpt54": "GPT-5.4",
|
|
76
|
-
"gpt55": "GPT-5.5",
|
|
77
|
-
"claude_sonnet": "Claude Sonnet 4.6",
|
|
78
|
-
"claude_opus": "Claude Opus 4.7",
|
|
79
|
-
"gemini_pro": "Gemini 3.1 Pro",
|
|
80
|
-
"nemotron": "Nemotron 3 Super",
|
|
81
|
-
"kimi_k26": "Kimi K2.6",
|
|
120
|
+
name: definition.display_name for name, definition in MODEL_METADATA.items()
|
|
82
121
|
}
|
|
83
122
|
|
|
84
123
|
THINKING_TOGGLEABLE: frozenset[str] = frozenset(
|
|
85
124
|
name for name, (base, thinking) in MODEL_MAP.items() if thinking is not None and thinking is not base
|
|
86
125
|
)
|
|
87
126
|
|
|
127
|
+
MAX_ONLY_MODEL_NAMES: frozenset[str] = frozenset(
|
|
128
|
+
name for name, definition in MODEL_METADATA.items() if definition.minimum_tier == "max"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
COUNCIL_ELIGIBLE_MODEL_NAMES: tuple[str, ...] = tuple(
|
|
132
|
+
name for name, definition in MODEL_METADATA.items() if definition.council_eligible
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
COUNCIL_DEFAULT_MODEL_NAMES: tuple[str, ...] = ("gpt54", "claude_sonnet", "gemini_pro")
|
|
136
|
+
COUNCIL_DEFAULT_MODELS_STR = ",".join(COUNCIL_DEFAULT_MODEL_NAMES)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def build_council_model_list(
|
|
140
|
+
model_names: tuple[str, ...] | list[str],
|
|
141
|
+
thinking: bool = False,
|
|
142
|
+
) -> list[tuple[str, Model]]:
|
|
143
|
+
"""Build display/model pairs for council execution from model metadata."""
|
|
144
|
+
model_list: list[tuple[str, Model]] = []
|
|
145
|
+
for name in model_names:
|
|
146
|
+
resolved = resolve_model(name, thinking=thinking)
|
|
147
|
+
display = COUNCIL_DISPLAY_NAMES.get(name, name)
|
|
148
|
+
if thinking and name in THINKING_TOGGLEABLE:
|
|
149
|
+
display += " Thinking"
|
|
150
|
+
model_list.append((display, resolved))
|
|
151
|
+
return model_list
|
|
152
|
+
|
|
88
153
|
|
|
89
154
|
def resolve_model(name: str, thinking: bool = False) -> Model:
|
|
90
155
|
"""Resolve a model name string to a Model instance.
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/api/server.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/cli/auth.py
RENAMED
|
File without changes
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/cli/doctor.py
RENAMED
|
File without changes
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/cli/hack.py
RENAMED
|
File without changes
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/cli/setup.py
RENAMED
|
File without changes
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/config.py
RENAMED
|
File without changes
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/constants.py
RENAMED
|
File without changes
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/core.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/enums.py
RENAMED
|
File without changes
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/exceptions.py
RENAMED
|
File without changes
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/http.py
RENAMED
|
File without changes
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/limits.py
RENAMED
|
File without changes
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/logging.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/models.py
RENAMED
|
File without changes
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/resilience.py
RENAMED
|
File without changes
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/router.py
RENAMED
|
File without changes
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/sessions.py
RENAMED
|
File without changes
|
|
File without changes
|
{perplexity_web_mcp_cli-0.11.2 → perplexity_web_mcp_cli-0.12.1}/src/perplexity_web_mcp/types.py
RENAMED
|
File without changes
|