feat: add report saving and display for Polymarket analysis
Save analysis results to disk with organized subfolders: 1_analysts/, 2_research/, 3_trading/, 4_risk/, 5_decision/ plus a consolidated complete_report.md. Prompt user to save and display full report after analysis completes, matching the existing stock analysis flow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4a11b4bf55
commit
90f1d1e120
139
cli/main.py
139
cli/main.py
|
|
@ -24,10 +24,8 @@ from rich.align import Align
|
||||||
from rich.rule import Rule
|
from rich.rule import Rule
|
||||||
|
|
||||||
from tradingagents.graph.trading_graph import TradingAgentsGraph
|
from tradingagents.graph.trading_graph import TradingAgentsGraph
|
||||||
from tradingagents.prediction_market import PMTradingAgentsGraph
|
|
||||||
from tradingagents.default_config import DEFAULT_CONFIG
|
from tradingagents.default_config import DEFAULT_CONFIG
|
||||||
from tradingagents.prediction_market.pm_config import PM_DEFAULT_CONFIG
|
from cli.models import AnalystType
|
||||||
from cli.models import AnalysisMode, AnalystType, PMAnalystType
|
|
||||||
from cli.utils import *
|
from cli.utils import *
|
||||||
from cli.announcements import fetch_announcements, display_announcements
|
from cli.announcements import fetch_announcements, display_announcements
|
||||||
from cli.stats_handler import StatsCallbackHandler
|
from cli.stats_handler import StatsCallbackHandler
|
||||||
|
|
@ -500,95 +498,62 @@ def get_user_selections():
|
||||||
box_content += f"\n[dim]Default: {default}[/dim]"
|
box_content += f"\n[dim]Default: {default}[/dim]"
|
||||||
return Panel(box_content, border_style="blue", padding=(1, 2))
|
return Panel(box_content, border_style="blue", padding=(1, 2))
|
||||||
|
|
||||||
# Step 1: Analysis mode (Stock or Polymarket)
|
# Step 1: Ticker symbol
|
||||||
console.print(
|
console.print(
|
||||||
create_question_box(
|
create_question_box(
|
||||||
"Step 1: Ticker Symbol or Polymarket Market ID",
|
"Step 1: Ticker Symbol", "Enter the ticker symbol to analyze", "SPY"
|
||||||
"Choose between stock analysis or prediction market analysis",
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
selected_mode = select_analysis_mode()
|
selected_ticker = get_ticker()
|
||||||
|
|
||||||
# Step 2: Ticker / Market ID based on mode
|
# Step 2: Analysis date
|
||||||
selected_ticker = None
|
|
||||||
market_id = None
|
|
||||||
market_question = ""
|
|
||||||
|
|
||||||
if selected_mode == AnalysisMode.STOCK:
|
|
||||||
console.print(
|
|
||||||
create_question_box(
|
|
||||||
"Step 2: Ticker Symbol", "Enter the ticker symbol to analyze", "SPY"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
selected_ticker = get_ticker()
|
|
||||||
else:
|
|
||||||
console.print(
|
|
||||||
create_question_box(
|
|
||||||
"Step 2: Polymarket Market",
|
|
||||||
"Paste a Polymarket URL or enter a numeric market ID",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
market_id, market_question = get_market_id()
|
|
||||||
|
|
||||||
# Step 3: Analysis date
|
|
||||||
default_date = datetime.datetime.now().strftime("%Y-%m-%d")
|
default_date = datetime.datetime.now().strftime("%Y-%m-%d")
|
||||||
console.print(
|
console.print(
|
||||||
create_question_box(
|
create_question_box(
|
||||||
"Step 3: Analysis Date",
|
"Step 2: Analysis Date",
|
||||||
"Enter the analysis date (YYYY-MM-DD)",
|
"Enter the analysis date (YYYY-MM-DD)",
|
||||||
default_date,
|
default_date,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
analysis_date = get_analysis_date()
|
analysis_date = get_analysis_date()
|
||||||
|
|
||||||
# Step 4: Select analysts
|
# Step 3: Select analysts
|
||||||
if selected_mode == AnalysisMode.STOCK:
|
|
||||||
console.print(
|
|
||||||
create_question_box(
|
|
||||||
"Step 4: Analysts Team", "Select your LLM analyst agents for the analysis"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
selected_analysts = select_analysts()
|
|
||||||
console.print(
|
|
||||||
f"[green]Selected analysts:[/green] {', '.join(analyst.value for analyst in selected_analysts)}"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
console.print(
|
|
||||||
create_question_box(
|
|
||||||
"Step 4: PM Analysts Team", "Select your prediction market analyst agents"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
selected_analysts = select_pm_analysts()
|
|
||||||
console.print(
|
|
||||||
f"[green]Selected PM analysts:[/green] {', '.join(analyst.value for analyst in selected_analysts)}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Step 5: Research depth
|
|
||||||
console.print(
|
console.print(
|
||||||
create_question_box(
|
create_question_box(
|
||||||
"Step 5: Research Depth", "Select your research depth level"
|
"Step 3: Analysts Team", "Select your LLM analyst agents for the analysis"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
selected_analysts = select_analysts()
|
||||||
|
console.print(
|
||||||
|
f"[green]Selected analysts:[/green] {', '.join(analyst.value for analyst in selected_analysts)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Step 4: Research depth
|
||||||
|
console.print(
|
||||||
|
create_question_box(
|
||||||
|
"Step 4: Research Depth", "Select your research depth level"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
selected_research_depth = select_research_depth()
|
selected_research_depth = select_research_depth()
|
||||||
|
|
||||||
# Step 6: LLM provider
|
# Step 5: OpenAI backend
|
||||||
console.print(
|
console.print(
|
||||||
create_question_box(
|
create_question_box(
|
||||||
"Step 6: LLM Provider", "Select which service to talk to"
|
"Step 5: OpenAI backend", "Select which service to talk to"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
selected_llm_provider, backend_url = select_llm_provider()
|
selected_llm_provider, backend_url = select_llm_provider()
|
||||||
|
|
||||||
# Step 7: Thinking agents
|
# Step 6: Thinking agents
|
||||||
console.print(
|
console.print(
|
||||||
create_question_box(
|
create_question_box(
|
||||||
"Step 7: Thinking Agents", "Select your thinking agents for analysis"
|
"Step 6: Thinking Agents", "Select your thinking agents for analysis"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
selected_shallow_thinker = select_shallow_thinking_agent(selected_llm_provider)
|
selected_shallow_thinker = select_shallow_thinking_agent(selected_llm_provider)
|
||||||
selected_deep_thinker = select_deep_thinking_agent(selected_llm_provider)
|
selected_deep_thinker = select_deep_thinking_agent(selected_llm_provider)
|
||||||
|
|
||||||
# Step 8: Provider-specific thinking configuration
|
# Step 7: Provider-specific thinking configuration
|
||||||
thinking_level = None
|
thinking_level = None
|
||||||
reasoning_effort = None
|
reasoning_effort = None
|
||||||
|
|
||||||
|
|
@ -596,7 +561,7 @@ def get_user_selections():
|
||||||
if provider_lower == "google":
|
if provider_lower == "google":
|
||||||
console.print(
|
console.print(
|
||||||
create_question_box(
|
create_question_box(
|
||||||
"Step 8: Thinking Mode",
|
"Step 7: Thinking Mode",
|
||||||
"Configure Gemini thinking mode"
|
"Configure Gemini thinking mode"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -604,17 +569,14 @@ def get_user_selections():
|
||||||
elif provider_lower == "openai":
|
elif provider_lower == "openai":
|
||||||
console.print(
|
console.print(
|
||||||
create_question_box(
|
create_question_box(
|
||||||
"Step 8: Reasoning Effort",
|
"Step 7: Reasoning Effort",
|
||||||
"Configure OpenAI reasoning effort level"
|
"Configure OpenAI reasoning effort level"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
reasoning_effort = ask_openai_reasoning_effort()
|
reasoning_effort = ask_openai_reasoning_effort()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"mode": selected_mode,
|
|
||||||
"ticker": selected_ticker,
|
"ticker": selected_ticker,
|
||||||
"market_id": market_id,
|
|
||||||
"market_question": market_question,
|
|
||||||
"analysis_date": analysis_date,
|
"analysis_date": analysis_date,
|
||||||
"analysts": selected_analysts,
|
"analysts": selected_analysts,
|
||||||
"research_depth": selected_research_depth,
|
"research_depth": selected_research_depth,
|
||||||
|
|
@ -938,53 +900,6 @@ def run_analysis():
|
||||||
# First get all user selections
|
# First get all user selections
|
||||||
selections = get_user_selections()
|
selections = get_user_selections()
|
||||||
|
|
||||||
# Branch to Polymarket flow if selected
|
|
||||||
if selections["mode"] == AnalysisMode.POLYMARKET:
|
|
||||||
config = PM_DEFAULT_CONFIG.copy()
|
|
||||||
config["max_debate_rounds"] = selections["research_depth"]
|
|
||||||
config["max_risk_discuss_rounds"] = selections["research_depth"]
|
|
||||||
config["quick_think_llm"] = selections["shallow_thinker"]
|
|
||||||
config["deep_think_llm"] = selections["deep_thinker"]
|
|
||||||
config["backend_url"] = selections["backend_url"]
|
|
||||||
config["llm_provider"] = selections["llm_provider"].lower()
|
|
||||||
config["google_thinking_level"] = selections.get("google_thinking_level")
|
|
||||||
config["openai_reasoning_effort"] = selections.get("openai_reasoning_effort")
|
|
||||||
|
|
||||||
pm_analyst_order = ["event", "odds", "information", "sentiment"]
|
|
||||||
selected_set = {analyst.value for analyst in selections["analysts"]}
|
|
||||||
selected_analyst_keys = [a for a in pm_analyst_order if a in selected_set]
|
|
||||||
|
|
||||||
pm_graph = PMTradingAgentsGraph(
|
|
||||||
selected_analyst_keys,
|
|
||||||
config=config,
|
|
||||||
debug=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
market_id = selections["market_id"]
|
|
||||||
market_question = selections.get("market_question", "")
|
|
||||||
analysis_date = selections["analysis_date"]
|
|
||||||
|
|
||||||
console.print(f"\n[bold cyan]Running Polymarket analysis...[/bold cyan]")
|
|
||||||
console.print(f" Market ID: [green]{market_id}[/green]")
|
|
||||||
if market_question:
|
|
||||||
console.print(f" Question: [green]{market_question}[/green]")
|
|
||||||
console.print(f" Date: [green]{analysis_date}[/green]")
|
|
||||||
console.print(f" Analysts: [green]{', '.join(selected_analyst_keys)}[/green]")
|
|
||||||
console.print()
|
|
||||||
|
|
||||||
_, decision = pm_graph.propagate(market_id, analysis_date, market_question)
|
|
||||||
|
|
||||||
console.print("\n[bold cyan]Analysis Complete![/bold cyan]\n")
|
|
||||||
console.print(Panel(
|
|
||||||
Markdown(f"```json\n{decision}\n```"),
|
|
||||||
title="[bold]Final Decision[/bold]",
|
|
||||||
border_style="green",
|
|
||||||
padding=(1, 2),
|
|
||||||
))
|
|
||||||
return
|
|
||||||
|
|
||||||
# --- Stock analysis flow (original) ---
|
|
||||||
|
|
||||||
# Create config with selected research depth
|
# Create config with selected research depth
|
||||||
config = DEFAULT_CONFIG.copy()
|
config = DEFAULT_CONFIG.copy()
|
||||||
config["max_debate_rounds"] = selections["research_depth"]
|
config["max_debate_rounds"] = selections["research_depth"]
|
||||||
|
|
|
||||||
|
|
@ -3,20 +3,8 @@ from typing import List, Optional, Dict
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
class AnalysisMode(str, Enum):
|
|
||||||
STOCK = "stock"
|
|
||||||
POLYMARKET = "polymarket"
|
|
||||||
|
|
||||||
|
|
||||||
class AnalystType(str, Enum):
|
class AnalystType(str, Enum):
|
||||||
MARKET = "market"
|
MARKET = "market"
|
||||||
SOCIAL = "social"
|
SOCIAL = "social"
|
||||||
NEWS = "news"
|
NEWS = "news"
|
||||||
FUNDAMENTALS = "fundamentals"
|
FUNDAMENTALS = "fundamentals"
|
||||||
|
|
||||||
|
|
||||||
class PMAnalystType(str, Enum):
|
|
||||||
EVENT = "event"
|
|
||||||
ODDS = "odds"
|
|
||||||
INFORMATION = "information"
|
|
||||||
SENTIMENT = "sentiment"
|
|
||||||
|
|
|
||||||
193
cli/utils.py
193
cli/utils.py
|
|
@ -3,12 +3,10 @@ from typing import List, Optional, Tuple, Dict
|
||||||
|
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
|
|
||||||
from cli.models import AnalysisMode, AnalystType, PMAnalystType
|
from cli.models import AnalystType
|
||||||
|
|
||||||
console = Console()
|
console = Console()
|
||||||
|
|
||||||
TICKER_INPUT_EXAMPLES = "Examples: SPY, CNC.TO, 7203.T, 0700.HK"
|
|
||||||
|
|
||||||
ANALYST_ORDER = [
|
ANALYST_ORDER = [
|
||||||
("Market Analyst", AnalystType.MARKET),
|
("Market Analyst", AnalystType.MARKET),
|
||||||
("Social Media Analyst", AnalystType.SOCIAL),
|
("Social Media Analyst", AnalystType.SOCIAL),
|
||||||
|
|
@ -16,173 +14,11 @@ ANALYST_ORDER = [
|
||||||
("Fundamentals Analyst", AnalystType.FUNDAMENTALS),
|
("Fundamentals Analyst", AnalystType.FUNDAMENTALS),
|
||||||
]
|
]
|
||||||
|
|
||||||
PM_ANALYST_ORDER = [
|
|
||||||
("Event Analyst", PMAnalystType.EVENT),
|
|
||||||
("Odds Analyst", PMAnalystType.ODDS),
|
|
||||||
("Information Analyst", PMAnalystType.INFORMATION),
|
|
||||||
("Sentiment Analyst", PMAnalystType.SENTIMENT),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def select_analysis_mode() -> AnalysisMode:
|
|
||||||
"""Select between Stock and Polymarket analysis."""
|
|
||||||
choice = questionary.select(
|
|
||||||
"Select Analysis Mode:",
|
|
||||||
choices=[
|
|
||||||
questionary.Choice("Stock Ticker (e.g. NVDA, TSLA)", value=AnalysisMode.STOCK),
|
|
||||||
questionary.Choice("Polymarket Market ID (prediction market)", value=AnalysisMode.POLYMARKET),
|
|
||||||
],
|
|
||||||
instruction="\n- Use arrow keys to navigate\n- Press Enter to select",
|
|
||||||
style=questionary.Style(
|
|
||||||
[
|
|
||||||
("selected", "fg:cyan noinherit"),
|
|
||||||
("highlighted", "fg:cyan noinherit"),
|
|
||||||
("pointer", "fg:cyan noinherit"),
|
|
||||||
]
|
|
||||||
),
|
|
||||||
).ask()
|
|
||||||
|
|
||||||
if choice is None:
|
|
||||||
console.print("\n[red]No mode selected. Exiting...[/red]")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
return choice
|
|
||||||
|
|
||||||
|
|
||||||
def _resolve_polymarket_url(url: str) -> tuple[str, str]:
|
|
||||||
"""Resolve a Polymarket URL to a (market_id, market_question) tuple.
|
|
||||||
|
|
||||||
Supports formats:
|
|
||||||
- https://polymarket.com/event/<event-slug>/<market-slug>
|
|
||||||
- https://polymarket.com/event/<market-slug>
|
|
||||||
"""
|
|
||||||
from urllib.parse import urlparse
|
|
||||||
import requests
|
|
||||||
|
|
||||||
parsed = urlparse(url)
|
|
||||||
parts = [p for p in parsed.path.split("/") if p]
|
|
||||||
|
|
||||||
if len(parts) < 2 or parts[0] != "event":
|
|
||||||
return "", ""
|
|
||||||
|
|
||||||
# Last segment is the market slug (or event slug if only 2 parts)
|
|
||||||
market_slug = parts[-1]
|
|
||||||
|
|
||||||
# Try as market slug first
|
|
||||||
try:
|
|
||||||
resp = requests.get(
|
|
||||||
"https://gamma-api.polymarket.com/markets",
|
|
||||||
params={"slug": market_slug},
|
|
||||||
timeout=15,
|
|
||||||
)
|
|
||||||
data = resp.json()
|
|
||||||
if isinstance(data, list) and data:
|
|
||||||
return str(data[0]["id"]), data[0].get("question", "")
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# If 3+ parts, the second segment is the event slug — resolve event and pick first market
|
|
||||||
if len(parts) >= 2:
|
|
||||||
event_slug = parts[1]
|
|
||||||
try:
|
|
||||||
resp = requests.get(
|
|
||||||
"https://gamma-api.polymarket.com/events",
|
|
||||||
params={"slug": event_slug},
|
|
||||||
timeout=15,
|
|
||||||
)
|
|
||||||
data = resp.json()
|
|
||||||
if isinstance(data, list) and data:
|
|
||||||
markets = data[0].get("markets", [])
|
|
||||||
if markets:
|
|
||||||
return str(markets[0]["id"]), markets[0].get("question", "")
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return "", ""
|
|
||||||
|
|
||||||
|
|
||||||
def get_market_id() -> tuple[str, str]:
|
|
||||||
"""Prompt the user to enter a Polymarket URL or market ID."""
|
|
||||||
user_input = questionary.text(
|
|
||||||
"Paste a Polymarket URL or enter a numeric market ID:",
|
|
||||||
validate=lambda x: len(x.strip()) > 0 or "Please enter a URL or market ID.",
|
|
||||||
style=questionary.Style(
|
|
||||||
[
|
|
||||||
("text", "fg:green"),
|
|
||||||
("highlighted", "noinherit"),
|
|
||||||
]
|
|
||||||
),
|
|
||||||
).ask()
|
|
||||||
|
|
||||||
if not user_input:
|
|
||||||
console.print("\n[red]No input provided. Exiting...[/red]")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
user_input = user_input.strip()
|
|
||||||
|
|
||||||
# Check if it's a URL
|
|
||||||
if "polymarket.com" in user_input:
|
|
||||||
console.print("[dim]Resolving Polymarket URL...[/dim]")
|
|
||||||
market_id, market_question = _resolve_polymarket_url(user_input)
|
|
||||||
if market_id:
|
|
||||||
console.print(f"[green]Found:[/green] {market_question} (ID: {market_id})")
|
|
||||||
return market_id, market_question
|
|
||||||
else:
|
|
||||||
console.print("[red]Could not resolve URL. Please enter a numeric market ID instead.[/red]")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
# Otherwise treat as numeric market ID
|
|
||||||
market_id = user_input
|
|
||||||
|
|
||||||
# Try to fetch the question from the API
|
|
||||||
market_question = ""
|
|
||||||
try:
|
|
||||||
import requests
|
|
||||||
resp = requests.get(
|
|
||||||
f"https://gamma-api.polymarket.com/markets/{market_id}",
|
|
||||||
timeout=15,
|
|
||||||
)
|
|
||||||
if resp.status_code == 200:
|
|
||||||
data = resp.json()
|
|
||||||
market_question = data.get("question", "")
|
|
||||||
if market_question:
|
|
||||||
console.print(f"[green]Found:[/green] {market_question}")
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return market_id, market_question
|
|
||||||
|
|
||||||
|
|
||||||
def select_pm_analysts() -> List[PMAnalystType]:
|
|
||||||
"""Select prediction market analysts using an interactive checkbox."""
|
|
||||||
choices = questionary.checkbox(
|
|
||||||
"Select Your [PM Analysts Team]:",
|
|
||||||
choices=[
|
|
||||||
questionary.Choice(display, value=value) for display, value in PM_ANALYST_ORDER
|
|
||||||
],
|
|
||||||
instruction="\n- Press Space to select/unselect analysts\n- Press 'a' to select/unselect all\n- Press Enter when done",
|
|
||||||
validate=lambda x: len(x) > 0 or "You must select at least one analyst.",
|
|
||||||
style=questionary.Style(
|
|
||||||
[
|
|
||||||
("checkbox-selected", "fg:green"),
|
|
||||||
("selected", "fg:green noinherit"),
|
|
||||||
("highlighted", "noinherit"),
|
|
||||||
("pointer", "noinherit"),
|
|
||||||
]
|
|
||||||
),
|
|
||||||
).ask()
|
|
||||||
|
|
||||||
if not choices:
|
|
||||||
console.print("\n[red]No analysts selected. Exiting...[/red]")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
return choices
|
|
||||||
|
|
||||||
|
|
||||||
def get_ticker() -> str:
|
def get_ticker() -> str:
|
||||||
"""Prompt the user to enter a ticker symbol."""
|
"""Prompt the user to enter a ticker symbol."""
|
||||||
ticker = questionary.text(
|
ticker = questionary.text(
|
||||||
f"Enter the exact ticker symbol to analyze ({TICKER_INPUT_EXAMPLES}):",
|
"Enter the ticker symbol to analyze:",
|
||||||
validate=lambda x: len(x.strip()) > 0 or "Please enter a valid ticker symbol.",
|
validate=lambda x: len(x.strip()) > 0 or "Please enter a valid ticker symbol.",
|
||||||
style=questionary.Style(
|
style=questionary.Style(
|
||||||
[
|
[
|
||||||
|
|
@ -196,11 +32,6 @@ def get_ticker() -> str:
|
||||||
console.print("\n[red]No ticker symbol provided. Exiting...[/red]")
|
console.print("\n[red]No ticker symbol provided. Exiting...[/red]")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
return normalize_ticker_symbol(ticker)
|
|
||||||
|
|
||||||
|
|
||||||
def normalize_ticker_symbol(ticker: str) -> str:
|
|
||||||
"""Normalize ticker input while preserving exchange suffixes."""
|
|
||||||
return ticker.strip().upper()
|
return ticker.strip().upper()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -480,26 +311,6 @@ def ask_openai_reasoning_effort() -> str:
|
||||||
).ask()
|
).ask()
|
||||||
|
|
||||||
|
|
||||||
def ask_anthropic_effort() -> str | None:
|
|
||||||
"""Ask for Anthropic effort level.
|
|
||||||
|
|
||||||
Controls token usage and response thoroughness on Claude 4.5+ and 4.6 models.
|
|
||||||
"""
|
|
||||||
return questionary.select(
|
|
||||||
"Select Effort Level:",
|
|
||||||
choices=[
|
|
||||||
questionary.Choice("High (recommended)", "high"),
|
|
||||||
questionary.Choice("Medium (balanced)", "medium"),
|
|
||||||
questionary.Choice("Low (faster, cheaper)", "low"),
|
|
||||||
],
|
|
||||||
style=questionary.Style([
|
|
||||||
("selected", "fg:cyan noinherit"),
|
|
||||||
("highlighted", "fg:cyan noinherit"),
|
|
||||||
("pointer", "fg:cyan noinherit"),
|
|
||||||
]),
|
|
||||||
).ask()
|
|
||||||
|
|
||||||
|
|
||||||
def ask_gemini_thinking_config() -> str | None:
|
def ask_gemini_thinking_config() -> str | None:
|
||||||
"""Ask for Gemini thinking configuration.
|
"""Ask for Gemini thinking configuration.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue