Upload folder using huggingface_hub
Browse files- ankigen_core/agents/base.py +14 -3
- ankigen_core/agents/config.py +3 -3
- ankigen_core/agents/schemas.py +1 -1
- ankigen_core/auto_config.py +8 -12
- ankigen_core/card_generator.py +9 -4
- ankigen_core/cli.py +5 -2
- ankigen_core/exporters.py +250 -354
- ankigen_core/llm_interface.py +183 -153
- pyproject.toml +3 -3
- requirements.txt +9 -13
- uv.lock +87 -60
ankigen_core/agents/base.py
CHANGED
|
@@ -36,7 +36,7 @@ class AgentConfig:
|
|
| 36 |
|
| 37 |
name: str
|
| 38 |
instructions: str
|
| 39 |
-
model: str = "gpt-
|
| 40 |
temperature: float = 0.7
|
| 41 |
max_tokens: Optional[int] = None
|
| 42 |
timeout: float = 30.0
|
|
@@ -67,8 +67,19 @@ class BaseAgentWrapper:
|
|
| 67 |
|
| 68 |
set_default_openai_client(self.openai_client, use_for_tracing=False)
|
| 69 |
|
| 70 |
-
# Create model settings with temperature
|
| 71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
|
| 73 |
# Use clean instructions without JSON formatting hacks
|
| 74 |
clean_instructions = self.config.instructions
|
|
|
|
| 36 |
|
| 37 |
name: str
|
| 38 |
instructions: str
|
| 39 |
+
model: str = "gpt-5.1"
|
| 40 |
temperature: float = 0.7
|
| 41 |
max_tokens: Optional[int] = None
|
| 42 |
timeout: float = 30.0
|
|
|
|
| 67 |
|
| 68 |
set_default_openai_client(self.openai_client, use_for_tracing=False)
|
| 69 |
|
| 70 |
+
# Create model settings with temperature and GPT-5.1 reasoning support
|
| 71 |
+
model_settings_kwargs = {"temperature": self.config.temperature}
|
| 72 |
+
|
| 73 |
+
# GPT-5.1 (not chat-latest) supports reasoning_effort
|
| 74 |
+
if (
|
| 75 |
+
self.config.model.startswith("gpt-5")
|
| 76 |
+
and "chat-latest" not in self.config.model
|
| 77 |
+
):
|
| 78 |
+
from openai.types.shared import Reasoning
|
| 79 |
+
|
| 80 |
+
model_settings_kwargs["reasoning"] = Reasoning(effort="none")
|
| 81 |
+
|
| 82 |
+
model_settings = ModelSettings(**model_settings_kwargs)
|
| 83 |
|
| 84 |
# Use clean instructions without JSON formatting hacks
|
| 85 |
clean_instructions = self.config.instructions
|
ankigen_core/agents/config.py
CHANGED
|
@@ -91,7 +91,7 @@ class AgentConfigManager:
|
|
| 91 |
|
| 92 |
# Default models for each agent type
|
| 93 |
default_models = {
|
| 94 |
-
"subject_expert_model": "gpt-
|
| 95 |
}
|
| 96 |
|
| 97 |
# Simple mapping: agent_name -> agent_name_model
|
|
@@ -111,7 +111,7 @@ class AgentConfigManager:
|
|
| 111 |
config = AgentConfig(
|
| 112 |
name=agent_data.get("name", agent_name),
|
| 113 |
instructions=agent_data.get("instructions", ""),
|
| 114 |
-
model=agent_data.get("model", "gpt-
|
| 115 |
temperature=agent_data.get("temperature", 0.7),
|
| 116 |
max_tokens=agent_data.get("max_tokens"),
|
| 117 |
timeout=agent_data.get("timeout", 30.0),
|
|
@@ -176,7 +176,7 @@ class AgentConfigManager:
|
|
| 176 |
config = AgentConfig(
|
| 177 |
name=agent_name,
|
| 178 |
instructions=agent_data.get("instructions", ""),
|
| 179 |
-
model=agent_data.get("model", "gpt-
|
| 180 |
temperature=agent_data.get("temperature", 0.7),
|
| 181 |
max_tokens=agent_data.get("max_tokens"),
|
| 182 |
timeout=agent_data.get("timeout", 30.0),
|
|
|
|
| 91 |
|
| 92 |
# Default models for each agent type
|
| 93 |
default_models = {
|
| 94 |
+
"subject_expert_model": "gpt-5.1",
|
| 95 |
}
|
| 96 |
|
| 97 |
# Simple mapping: agent_name -> agent_name_model
|
|
|
|
| 111 |
config = AgentConfig(
|
| 112 |
name=agent_data.get("name", agent_name),
|
| 113 |
instructions=agent_data.get("instructions", ""),
|
| 114 |
+
model=agent_data.get("model", "gpt-5.1"),
|
| 115 |
temperature=agent_data.get("temperature", 0.7),
|
| 116 |
max_tokens=agent_data.get("max_tokens"),
|
| 117 |
timeout=agent_data.get("timeout", 30.0),
|
|
|
|
| 176 |
config = AgentConfig(
|
| 177 |
name=agent_name,
|
| 178 |
instructions=agent_data.get("instructions", ""),
|
| 179 |
+
model=agent_data.get("model", "gpt-5.1"),
|
| 180 |
temperature=agent_data.get("temperature", 0.7),
|
| 181 |
max_tokens=agent_data.get("max_tokens"),
|
| 182 |
timeout=agent_data.get("timeout", 30.0),
|
ankigen_core/agents/schemas.py
CHANGED
|
@@ -167,7 +167,7 @@ class AutoConfigSchema(BaseModel):
|
|
| 167 |
)
|
| 168 |
model_choice: str = Field(
|
| 169 |
...,
|
| 170 |
-
description="Recommended model: 'gpt-
|
| 171 |
)
|
| 172 |
|
| 173 |
# Analysis metadata
|
|
|
|
| 167 |
)
|
| 168 |
model_choice: str = Field(
|
| 169 |
...,
|
| 170 |
+
description="Recommended model: 'gpt-5.1' for all tasks (uses reasoning_effort=none for speed)",
|
| 171 |
)
|
| 172 |
|
| 173 |
# Analysis metadata
|
ankigen_core/auto_config.py
CHANGED
|
@@ -6,6 +6,7 @@ from openai import AsyncOpenAI
|
|
| 6 |
from ankigen_core.logging import logger
|
| 7 |
from ankigen_core.context7 import Context7Client
|
| 8 |
from ankigen_core.agents.schemas import AutoConfigSchema
|
|
|
|
| 9 |
|
| 10 |
|
| 11 |
class AutoConfigService:
|
|
@@ -70,20 +71,15 @@ Extract:
|
|
| 70 |
Provide a brief rationale for your choices."""
|
| 71 |
|
| 72 |
try:
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
response_format=AutoConfigSchema,
|
| 80 |
temperature=0.3, # Lower temperature for more consistent analysis
|
| 81 |
)
|
| 82 |
|
| 83 |
-
if not response.choices or not response.choices[0].message.parsed:
|
| 84 |
-
raise ValueError("Failed to get valid response from OpenAI")
|
| 85 |
-
|
| 86 |
-
config = response.choices[0].message.parsed
|
| 87 |
logger.info(
|
| 88 |
f"Subject analysis complete: library='{config.library_search_term}', "
|
| 89 |
f"topics={config.topic_number}, cards/topic={config.cards_per_topic}"
|
|
@@ -100,7 +96,7 @@ Provide a brief rationale for your choices."""
|
|
| 100 |
cards_per_topic=8,
|
| 101 |
learning_preferences="Focus on fundamental concepts and core principles with practical examples",
|
| 102 |
generate_cloze=False,
|
| 103 |
-
model_choice="gpt-
|
| 104 |
subject_type="concepts",
|
| 105 |
scope="medium",
|
| 106 |
rationale="Using default settings due to analysis error",
|
|
|
|
| 6 |
from ankigen_core.logging import logger
|
| 7 |
from ankigen_core.context7 import Context7Client
|
| 8 |
from ankigen_core.agents.schemas import AutoConfigSchema
|
| 9 |
+
from ankigen_core.llm_interface import structured_agent_call
|
| 10 |
|
| 11 |
|
| 12 |
class AutoConfigService:
|
|
|
|
| 71 |
Provide a brief rationale for your choices."""
|
| 72 |
|
| 73 |
try:
|
| 74 |
+
config = await structured_agent_call(
|
| 75 |
+
openai_client=openai_client,
|
| 76 |
+
model="gpt-5.1",
|
| 77 |
+
instructions=system_prompt,
|
| 78 |
+
user_input=user_prompt,
|
| 79 |
+
output_type=AutoConfigSchema,
|
|
|
|
| 80 |
temperature=0.3, # Lower temperature for more consistent analysis
|
| 81 |
)
|
| 82 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
logger.info(
|
| 84 |
f"Subject analysis complete: library='{config.library_search_term}', "
|
| 85 |
f"topics={config.topic_number}, cards/topic={config.cards_per_topic}"
|
|
|
|
| 96 |
cards_per_topic=8,
|
| 97 |
learning_preferences="Focus on fundamental concepts and core principles with practical examples",
|
| 98 |
generate_cloze=False,
|
| 99 |
+
model_choice="gpt-5.1",
|
| 100 |
subject_type="concepts",
|
| 101 |
scope="medium",
|
| 102 |
rationale="Using default settings due to analysis error",
|
ankigen_core/card_generator.py
CHANGED
|
@@ -29,15 +29,20 @@ logger.info("Agent system loaded successfully")
|
|
| 29 |
|
| 30 |
# --- Constants --- (Moved from app.py)
|
| 31 |
AVAILABLE_MODELS = [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
{
|
| 33 |
"value": "gpt-4.1",
|
| 34 |
-
"label": "GPT-4.1 (
|
| 35 |
-
"description": "
|
| 36 |
},
|
| 37 |
{
|
| 38 |
"value": "gpt-4.1-nano",
|
| 39 |
-
"label": "GPT-4.1 Nano (
|
| 40 |
-
"description": "
|
| 41 |
},
|
| 42 |
]
|
| 43 |
|
|
|
|
| 29 |
|
| 30 |
# --- Constants --- (Moved from app.py)
|
| 31 |
AVAILABLE_MODELS = [
|
| 32 |
+
{
|
| 33 |
+
"value": "gpt-5.1",
|
| 34 |
+
"label": "GPT-5.1 (Best Quality)",
|
| 35 |
+
"description": "Latest model with adaptive reasoning, 400K context",
|
| 36 |
+
},
|
| 37 |
{
|
| 38 |
"value": "gpt-4.1",
|
| 39 |
+
"label": "GPT-4.1 (Legacy)",
|
| 40 |
+
"description": "Previous generation, large context window",
|
| 41 |
},
|
| 42 |
{
|
| 43 |
"value": "gpt-4.1-nano",
|
| 44 |
+
"label": "GPT-4.1 Nano (Legacy Fast)",
|
| 45 |
+
"description": "Previous generation, ultra-fast",
|
| 46 |
},
|
| 47 |
]
|
| 48 |
|
ankigen_core/cli.py
CHANGED
|
@@ -131,7 +131,7 @@ async def generate_cards_from_config(
|
|
| 131 |
generation_mode="subject",
|
| 132 |
source_text="",
|
| 133 |
url_input="",
|
| 134 |
-
model_name=config.get("model_choice", "gpt-
|
| 135 |
topic_number=config.get("topic_number", 3),
|
| 136 |
cards_per_topic=config.get("cards_per_topic", 5),
|
| 137 |
preference_prompt=config.get("preference_prompt", ""),
|
|
@@ -207,7 +207,10 @@ def export_cards(
|
|
| 207 |
)
|
| 208 |
@click.option(
|
| 209 |
"--model",
|
| 210 |
-
type=click.Choice(
|
|
|
|
|
|
|
|
|
|
| 211 |
help="Model to use for generation (auto-selected if not specified)",
|
| 212 |
)
|
| 213 |
@click.option(
|
|
|
|
| 131 |
generation_mode="subject",
|
| 132 |
source_text="",
|
| 133 |
url_input="",
|
| 134 |
+
model_name=config.get("model_choice", "gpt-5.1"),
|
| 135 |
topic_number=config.get("topic_number", 3),
|
| 136 |
cards_per_topic=config.get("cards_per_topic", 5),
|
| 137 |
preference_prompt=config.get("preference_prompt", ""),
|
|
|
|
| 207 |
)
|
| 208 |
@click.option(
|
| 209 |
"--model",
|
| 210 |
+
type=click.Choice(
|
| 211 |
+
["gpt-5.1", "gpt-4.1", "gpt-4.1-nano"],
|
| 212 |
+
case_sensitive=False,
|
| 213 |
+
),
|
| 214 |
help="Model to use for generation (auto-selected if not specified)",
|
| 215 |
)
|
| 216 |
@click.option(
|
ankigen_core/exporters.py
CHANGED
|
@@ -84,7 +84,253 @@ ANKI_CLOZE_MODEL_NAME = "AnkiGen Cloze"
|
|
| 84 |
DEFAULT_BASIC_MODEL_ID = random.randrange(1 << 30, 1 << 31)
|
| 85 |
DEFAULT_CLOZE_MODEL_ID = random.randrange(1 << 30, 1 << 31)
|
| 86 |
|
| 87 |
-
# ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
BASIC_MODEL = genanki.Model(
|
| 90 |
DEFAULT_BASIC_MODEL_ID, # Use the generated ID
|
|
@@ -167,188 +413,7 @@ BASIC_MODEL = genanki.Model(
|
|
| 167 |
""",
|
| 168 |
}
|
| 169 |
],
|
| 170 |
-
css=
|
| 171 |
-
/* Base styles */
|
| 172 |
-
.card {
|
| 173 |
-
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
| 174 |
-
font-size: 16px;
|
| 175 |
-
line-height: 1.6;
|
| 176 |
-
color: #1a1a1a;
|
| 177 |
-
max-width: 800px;
|
| 178 |
-
margin: 0 auto;
|
| 179 |
-
padding: 20px;
|
| 180 |
-
background: #ffffff;
|
| 181 |
-
}
|
| 182 |
-
|
| 183 |
-
@media (max-width: 768px) {
|
| 184 |
-
.card {
|
| 185 |
-
font-size: 14px;
|
| 186 |
-
padding: 15px;
|
| 187 |
-
}
|
| 188 |
-
}
|
| 189 |
-
|
| 190 |
-
/* Question side */
|
| 191 |
-
.question-side {
|
| 192 |
-
position: relative;
|
| 193 |
-
min-height: 200px;
|
| 194 |
-
}
|
| 195 |
-
|
| 196 |
-
.difficulty-indicator {
|
| 197 |
-
position: absolute;
|
| 198 |
-
top: 10px;
|
| 199 |
-
right: 10px;
|
| 200 |
-
width: 10px;
|
| 201 |
-
height: 10px;
|
| 202 |
-
border-radius: 50%;
|
| 203 |
-
}
|
| 204 |
-
|
| 205 |
-
.difficulty-indicator.beginner { background: #4ade80; }
|
| 206 |
-
.difficulty-indicator.intermediate { background: #fbbf24; }
|
| 207 |
-
.difficulty-indicator.advanced { background: #ef4444; }
|
| 208 |
-
|
| 209 |
-
.question {
|
| 210 |
-
font-size: 1.3em;
|
| 211 |
-
font-weight: 600;
|
| 212 |
-
color: #2563eb;
|
| 213 |
-
margin-bottom: 1.5em;
|
| 214 |
-
}
|
| 215 |
-
|
| 216 |
-
.prerequisites {
|
| 217 |
-
margin-top: 1em;
|
| 218 |
-
font-size: 0.9em;
|
| 219 |
-
color: #666;
|
| 220 |
-
}
|
| 221 |
-
|
| 222 |
-
.prerequisites-toggle {
|
| 223 |
-
color: #2563eb;
|
| 224 |
-
cursor: pointer;
|
| 225 |
-
text-decoration: underline;
|
| 226 |
-
}
|
| 227 |
-
|
| 228 |
-
.prerequisites-content {
|
| 229 |
-
display: none;
|
| 230 |
-
margin-top: 0.5em;
|
| 231 |
-
padding: 0.5em;
|
| 232 |
-
background: #f8fafc;
|
| 233 |
-
border-radius: 4px;
|
| 234 |
-
}
|
| 235 |
-
|
| 236 |
-
.prerequisites.show .prerequisites-content {
|
| 237 |
-
display: block;
|
| 238 |
-
}
|
| 239 |
-
|
| 240 |
-
/* Answer side */
|
| 241 |
-
.answer-section,
|
| 242 |
-
.explanation-section,
|
| 243 |
-
.example-section {
|
| 244 |
-
margin: 1.5em 0;
|
| 245 |
-
padding: 1.2em;
|
| 246 |
-
border-radius: 8px;
|
| 247 |
-
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
| 248 |
-
}
|
| 249 |
-
|
| 250 |
-
.answer-section {
|
| 251 |
-
background: #f0f9ff;
|
| 252 |
-
border-left: 4px solid #2563eb;
|
| 253 |
-
}
|
| 254 |
-
|
| 255 |
-
.explanation-section {
|
| 256 |
-
background: #f0fdf4;
|
| 257 |
-
border-left: 4px solid #4ade80;
|
| 258 |
-
}
|
| 259 |
-
|
| 260 |
-
.example-section {
|
| 261 |
-
background: #fefce8; /* Light yellow */
|
| 262 |
-
border-left: 4px solid #facc15; /* Yellow */
|
| 263 |
-
}
|
| 264 |
-
.example-section pre {
|
| 265 |
-
background-color: #2d2d2d; /* Darker background for code blocks */
|
| 266 |
-
color: #f8f8f2; /* Light text for contrast */
|
| 267 |
-
padding: 1em;
|
| 268 |
-
border-radius: 0.3em;
|
| 269 |
-
overflow-x: auto; /* Horizontal scroll for long lines */
|
| 270 |
-
font-family: 'Consolas', 'Monaco', 'Menlo', monospace;
|
| 271 |
-
font-size: 0.9em;
|
| 272 |
-
line-height: 1.4;
|
| 273 |
-
}
|
| 274 |
-
|
| 275 |
-
.example-section code {
|
| 276 |
-
font-family: 'Consolas', 'Monaco', 'Menlo', monospace;
|
| 277 |
-
}
|
| 278 |
-
|
| 279 |
-
.metadata-section {
|
| 280 |
-
margin-top: 2em;
|
| 281 |
-
padding-top: 1em;
|
| 282 |
-
border-top: 1px solid #e5e7eb; /* Light gray border */
|
| 283 |
-
font-size: 0.9em;
|
| 284 |
-
color: #4b5563; /* Cool gray */
|
| 285 |
-
}
|
| 286 |
-
|
| 287 |
-
.metadata-section h3 {
|
| 288 |
-
font-size: 1em;
|
| 289 |
-
color: #1f2937; /* Darker gray for headings */
|
| 290 |
-
margin-bottom: 0.5em;
|
| 291 |
-
}
|
| 292 |
-
|
| 293 |
-
.metadata-section > div {
|
| 294 |
-
margin-bottom: 0.8em;
|
| 295 |
-
}
|
| 296 |
-
|
| 297 |
-
.source-url a {
|
| 298 |
-
color: #2563eb;
|
| 299 |
-
text-decoration: none;
|
| 300 |
-
}
|
| 301 |
-
.source-url a:hover {
|
| 302 |
-
text-decoration: underline;
|
| 303 |
-
}
|
| 304 |
-
|
| 305 |
-
/* Styles for cloze deletion cards */
|
| 306 |
-
.cloze {
|
| 307 |
-
font-weight: bold;
|
| 308 |
-
color: blue;
|
| 309 |
-
}
|
| 310 |
-
.nightMode .cloze {
|
| 311 |
-
color: lightblue;
|
| 312 |
-
}
|
| 313 |
-
|
| 314 |
-
/* General utility */
|
| 315 |
-
hr {
|
| 316 |
-
border: none;
|
| 317 |
-
border-top: 1px dashed #cbd5e1; /* Light dashed line */
|
| 318 |
-
margin: 1.5em 0;
|
| 319 |
-
}
|
| 320 |
-
|
| 321 |
-
/* Rich text field styling (if Anki adds classes for these) */
|
| 322 |
-
.field ul, .field ol {
|
| 323 |
-
margin-left: 1.5em;
|
| 324 |
-
padding-left: 0.5em;
|
| 325 |
-
}
|
| 326 |
-
.field li {
|
| 327 |
-
margin-bottom: 0.3em;
|
| 328 |
-
}
|
| 329 |
-
|
| 330 |
-
/* Responsive design */
|
| 331 |
-
@media (max-width: 640px) {
|
| 332 |
-
.answer-section,
|
| 333 |
-
.explanation-section,
|
| 334 |
-
.example-section {
|
| 335 |
-
padding: 1em;
|
| 336 |
-
margin: 1em 0;
|
| 337 |
-
}
|
| 338 |
-
}
|
| 339 |
-
|
| 340 |
-
/* Animations */
|
| 341 |
-
@keyframes fadeIn {
|
| 342 |
-
from { opacity: 0; }
|
| 343 |
-
to { opacity: 1; }
|
| 344 |
-
}
|
| 345 |
-
|
| 346 |
-
.card {
|
| 347 |
-
animation: fadeIn 0.3s ease-in-out;
|
| 348 |
-
}
|
| 349 |
-
""",
|
| 350 |
-
# model_type=genanki.Model.BASIC, # This was still incorrect
|
| 351 |
-
# No model_type needed, defaults to Basic (0)
|
| 352 |
)
|
| 353 |
|
| 354 |
CLOZE_MODEL = genanki.Model(
|
|
@@ -432,177 +497,8 @@ CLOZE_MODEL = genanki.Model(
|
|
| 432 |
""",
|
| 433 |
}
|
| 434 |
],
|
| 435 |
-
css=
|
| 436 |
-
|
| 437 |
-
.card {
|
| 438 |
-
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
| 439 |
-
font-size: 16px;
|
| 440 |
-
line-height: 1.6;
|
| 441 |
-
color: #1a1a1a;
|
| 442 |
-
max-width: 800px;
|
| 443 |
-
margin: 0 auto;
|
| 444 |
-
padding: 20px;
|
| 445 |
-
background: #ffffff;
|
| 446 |
-
}
|
| 447 |
-
|
| 448 |
-
@media (max-width: 768px) {
|
| 449 |
-
.card {
|
| 450 |
-
font-size: 14px;
|
| 451 |
-
padding: 15px;
|
| 452 |
-
}
|
| 453 |
-
}
|
| 454 |
-
|
| 455 |
-
/* Question side */
|
| 456 |
-
.question-side {
|
| 457 |
-
position: relative;
|
| 458 |
-
min-height: 200px;
|
| 459 |
-
}
|
| 460 |
-
|
| 461 |
-
.difficulty-indicator {
|
| 462 |
-
position: absolute;
|
| 463 |
-
top: 10px;
|
| 464 |
-
right: 10px;
|
| 465 |
-
width: 10px;
|
| 466 |
-
height: 10px;
|
| 467 |
-
border-radius: 50%;
|
| 468 |
-
}
|
| 469 |
-
|
| 470 |
-
.difficulty-indicator.beginner { background: #4ade80; }
|
| 471 |
-
.difficulty-indicator.intermediate { background: #fbbf24; }
|
| 472 |
-
.difficulty-indicator.advanced { background: #ef4444; }
|
| 473 |
-
|
| 474 |
-
.question {
|
| 475 |
-
font-size: 1.3em;
|
| 476 |
-
font-weight: 600;
|
| 477 |
-
color: #2563eb;
|
| 478 |
-
margin-bottom: 1.5em;
|
| 479 |
-
}
|
| 480 |
-
|
| 481 |
-
.prerequisites {
|
| 482 |
-
margin-top: 1em;
|
| 483 |
-
font-size: 0.9em;
|
| 484 |
-
color: #666;
|
| 485 |
-
}
|
| 486 |
-
|
| 487 |
-
.prerequisites-toggle {
|
| 488 |
-
color: #2563eb;
|
| 489 |
-
cursor: pointer;
|
| 490 |
-
text-decoration: underline;
|
| 491 |
-
}
|
| 492 |
-
|
| 493 |
-
.prerequisites-content {
|
| 494 |
-
display: none;
|
| 495 |
-
margin-top: 0.5em;
|
| 496 |
-
padding: 0.5em;
|
| 497 |
-
background: #f8fafc;
|
| 498 |
-
border-radius: 4px;
|
| 499 |
-
}
|
| 500 |
-
|
| 501 |
-
.prerequisites.show .prerequisites-content {
|
| 502 |
-
display: block;
|
| 503 |
-
}
|
| 504 |
-
|
| 505 |
-
/* Answer side */
|
| 506 |
-
.answer-section,
|
| 507 |
-
.explanation-section,
|
| 508 |
-
.example-section {
|
| 509 |
-
margin: 1.5em 0;
|
| 510 |
-
padding: 1.2em;
|
| 511 |
-
border-radius: 8px;
|
| 512 |
-
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
| 513 |
-
}
|
| 514 |
-
|
| 515 |
-
.answer-section { /* Shared with question for cloze, but can be general */
|
| 516 |
-
background: #f0f9ff;
|
| 517 |
-
border-left: 4px solid #2563eb;
|
| 518 |
-
}
|
| 519 |
-
|
| 520 |
-
.back-extra-section {
|
| 521 |
-
background: #eef2ff; /* A slightly different shade for additional info */
|
| 522 |
-
border-left: 4px solid #818cf8; /* Indigo variant */
|
| 523 |
-
margin: 1.5em 0;
|
| 524 |
-
padding: 1.2em;
|
| 525 |
-
border-radius: 8px;
|
| 526 |
-
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
| 527 |
-
}
|
| 528 |
-
|
| 529 |
-
.explanation-section {
|
| 530 |
-
background: #f0fdf4;
|
| 531 |
-
border-left: 4px solid #4ade80;
|
| 532 |
-
}
|
| 533 |
-
|
| 534 |
-
.example-section {
|
| 535 |
-
background: #fefce8; /* Light yellow */
|
| 536 |
-
border-left: 4px solid #facc15; /* Yellow */
|
| 537 |
-
}
|
| 538 |
-
.example-section pre {
|
| 539 |
-
background-color: #2d2d2d; /* Darker background for code blocks */
|
| 540 |
-
color: #f8f8f2; /* Light text for contrast */
|
| 541 |
-
padding: 1em;
|
| 542 |
-
border-radius: 0.3em;
|
| 543 |
-
overflow-x: auto; /* Horizontal scroll for long lines */
|
| 544 |
-
font-family: 'Consolas', 'Monaco', 'Menlo', monospace;
|
| 545 |
-
font-size: 0.9em;
|
| 546 |
-
line-height: 1.4;
|
| 547 |
-
}
|
| 548 |
-
|
| 549 |
-
.example-section code {
|
| 550 |
-
font-family: 'Consolas', 'Monaco', 'Menlo', monospace;
|
| 551 |
-
}
|
| 552 |
-
|
| 553 |
-
.metadata-section {
|
| 554 |
-
margin-top: 2em;
|
| 555 |
-
padding-top: 1em;
|
| 556 |
-
border-top: 1px solid #e5e7eb; /* Light gray border */
|
| 557 |
-
font-size: 0.9em;
|
| 558 |
-
color: #4b5563; /* Cool gray */
|
| 559 |
-
}
|
| 560 |
-
|
| 561 |
-
.metadata-section h3 {
|
| 562 |
-
font-size: 1em;
|
| 563 |
-
color: #1f2937; /* Darker gray for headings */
|
| 564 |
-
margin-bottom: 0.5em;
|
| 565 |
-
}
|
| 566 |
-
|
| 567 |
-
.metadata-section > div {
|
| 568 |
-
margin-bottom: 0.8em;
|
| 569 |
-
}
|
| 570 |
-
|
| 571 |
-
.source-url a {
|
| 572 |
-
color: #2563eb;
|
| 573 |
-
text-decoration: none;
|
| 574 |
-
}
|
| 575 |
-
.source-url a:hover {
|
| 576 |
-
text-decoration: underline;
|
| 577 |
-
}
|
| 578 |
-
|
| 579 |
-
/* Styles for cloze deletion cards */
|
| 580 |
-
.cloze {
|
| 581 |
-
font-weight: bold;
|
| 582 |
-
color: blue;
|
| 583 |
-
}
|
| 584 |
-
.nightMode .cloze {
|
| 585 |
-
color: lightblue;
|
| 586 |
-
}
|
| 587 |
-
|
| 588 |
-
/* General utility */
|
| 589 |
-
hr {
|
| 590 |
-
border: none;
|
| 591 |
-
border-top: 1px dashed #cbd5e1; /* Light dashed line */
|
| 592 |
-
margin: 1.5em 0;
|
| 593 |
-
}
|
| 594 |
-
|
| 595 |
-
/* Rich text field styling (if Anki adds classes for these) */
|
| 596 |
-
.field ul, .field ol {
|
| 597 |
-
margin-left: 1.5em;
|
| 598 |
-
padding-left: 0.5em;
|
| 599 |
-
}
|
| 600 |
-
.field li {
|
| 601 |
-
margin-bottom: 0.3em;
|
| 602 |
-
}
|
| 603 |
-
""",
|
| 604 |
-
# model_type=genanki.Model.CLOZE, # This was still incorrect
|
| 605 |
-
model_type=1, # Corrected to use integer 1 for Cloze
|
| 606 |
)
|
| 607 |
|
| 608 |
|
|
|
|
| 84 |
DEFAULT_BASIC_MODEL_ID = random.randrange(1 << 30, 1 << 31)
|
| 85 |
DEFAULT_CLOZE_MODEL_ID = random.randrange(1 << 30, 1 << 31)
|
| 86 |
|
| 87 |
+
# --- Shared CSS with dark mode support ---
|
| 88 |
+
CARD_CSS = """
|
| 89 |
+
/* CSS Variables - Light Mode (default) */
|
| 90 |
+
.card {
|
| 91 |
+
--bg-card: #ffffff;
|
| 92 |
+
--bg-answer: #f0f9ff;
|
| 93 |
+
--bg-explanation: #f0fdf4;
|
| 94 |
+
--bg-example: #fefce8;
|
| 95 |
+
--bg-back-extra: #eef2ff;
|
| 96 |
+
--bg-prereq: #f8fafc;
|
| 97 |
+
--bg-code: #2d2d2d;
|
| 98 |
+
|
| 99 |
+
--text-primary: #1a1a1a;
|
| 100 |
+
--text-secondary: #4b5563;
|
| 101 |
+
--text-muted: #666666;
|
| 102 |
+
--text-heading: #1f2937;
|
| 103 |
+
--text-code: #f8f8f2;
|
| 104 |
+
|
| 105 |
+
--accent-blue: #2563eb;
|
| 106 |
+
--accent-blue-light: #60a5fa;
|
| 107 |
+
--accent-green: #4ade80;
|
| 108 |
+
--accent-yellow: #facc15;
|
| 109 |
+
--accent-indigo: #818cf8;
|
| 110 |
+
--accent-red: #ef4444;
|
| 111 |
+
|
| 112 |
+
--border-light: #e5e7eb;
|
| 113 |
+
--border-dashed: #cbd5e1;
|
| 114 |
+
|
| 115 |
+
--shadow: rgba(0, 0, 0, 0.05);
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
/* Dark Mode Overrides */
|
| 119 |
+
.nightMode .card,
|
| 120 |
+
.night_mode .card {
|
| 121 |
+
--bg-card: #1e1e1e;
|
| 122 |
+
--bg-answer: #1e293b;
|
| 123 |
+
--bg-explanation: #14291a;
|
| 124 |
+
--bg-example: #292518;
|
| 125 |
+
--bg-back-extra: #1e1b2e;
|
| 126 |
+
--bg-prereq: #262626;
|
| 127 |
+
--bg-code: #0d0d0d;
|
| 128 |
+
|
| 129 |
+
--text-primary: #e4e4e7;
|
| 130 |
+
--text-secondary: #a1a1aa;
|
| 131 |
+
--text-muted: #9ca3af;
|
| 132 |
+
--text-heading: #f4f4f5;
|
| 133 |
+
--text-code: #f8f8f2;
|
| 134 |
+
|
| 135 |
+
--accent-blue: #60a5fa;
|
| 136 |
+
--accent-blue-light: #93c5fd;
|
| 137 |
+
--accent-green: #4ade80;
|
| 138 |
+
--accent-yellow: #fde047;
|
| 139 |
+
--accent-indigo: #a5b4fc;
|
| 140 |
+
--accent-red: #f87171;
|
| 141 |
+
|
| 142 |
+
--border-light: #3f3f46;
|
| 143 |
+
--border-dashed: #52525b;
|
| 144 |
+
|
| 145 |
+
--shadow: rgba(0, 0, 0, 0.3);
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
/* Base styles */
|
| 149 |
+
.card {
|
| 150 |
+
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
| 151 |
+
font-size: 16px;
|
| 152 |
+
line-height: 1.6;
|
| 153 |
+
color: var(--text-primary);
|
| 154 |
+
max-width: 800px;
|
| 155 |
+
margin: 0 auto;
|
| 156 |
+
padding: 20px;
|
| 157 |
+
background: var(--bg-card);
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
@media (max-width: 768px) {
|
| 161 |
+
.card {
|
| 162 |
+
font-size: 14px;
|
| 163 |
+
padding: 15px;
|
| 164 |
+
}
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
/* Question side */
|
| 168 |
+
.question-side {
|
| 169 |
+
position: relative;
|
| 170 |
+
min-height: 200px;
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
.difficulty-indicator {
|
| 174 |
+
position: absolute;
|
| 175 |
+
top: 10px;
|
| 176 |
+
right: 10px;
|
| 177 |
+
width: 10px;
|
| 178 |
+
height: 10px;
|
| 179 |
+
border-radius: 50%;
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
.difficulty-indicator.beginner { background: var(--accent-green); }
|
| 183 |
+
.difficulty-indicator.intermediate { background: var(--accent-yellow); }
|
| 184 |
+
.difficulty-indicator.advanced { background: var(--accent-red); }
|
| 185 |
+
|
| 186 |
+
.question {
|
| 187 |
+
font-size: 1.3em;
|
| 188 |
+
font-weight: 600;
|
| 189 |
+
color: var(--accent-blue);
|
| 190 |
+
margin-bottom: 1.5em;
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
.prerequisites {
|
| 194 |
+
margin-top: 1em;
|
| 195 |
+
font-size: 0.9em;
|
| 196 |
+
color: var(--text-muted);
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
.prerequisites-toggle {
|
| 200 |
+
color: var(--accent-blue);
|
| 201 |
+
cursor: pointer;
|
| 202 |
+
text-decoration: underline;
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
.prerequisites-content {
|
| 206 |
+
display: none;
|
| 207 |
+
margin-top: 0.5em;
|
| 208 |
+
padding: 0.5em;
|
| 209 |
+
background: var(--bg-prereq);
|
| 210 |
+
border-radius: 4px;
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
.prerequisites.show .prerequisites-content {
|
| 214 |
+
display: block;
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
/* Answer side sections */
|
| 218 |
+
.answer-section,
|
| 219 |
+
.explanation-section,
|
| 220 |
+
.example-section,
|
| 221 |
+
.back-extra-section {
|
| 222 |
+
margin: 1.5em 0;
|
| 223 |
+
padding: 1.2em;
|
| 224 |
+
border-radius: 8px;
|
| 225 |
+
box-shadow: 0 2px 4px var(--shadow);
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
.answer-section {
|
| 229 |
+
background: var(--bg-answer);
|
| 230 |
+
border-left: 4px solid var(--accent-blue);
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
.back-extra-section {
|
| 234 |
+
background: var(--bg-back-extra);
|
| 235 |
+
border-left: 4px solid var(--accent-indigo);
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
+
.explanation-section {
|
| 239 |
+
background: var(--bg-explanation);
|
| 240 |
+
border-left: 4px solid var(--accent-green);
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
.example-section {
|
| 244 |
+
background: var(--bg-example);
|
| 245 |
+
border-left: 4px solid var(--accent-yellow);
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
.example-section pre {
|
| 249 |
+
background-color: var(--bg-code);
|
| 250 |
+
color: var(--text-code);
|
| 251 |
+
padding: 1em;
|
| 252 |
+
border-radius: 0.3em;
|
| 253 |
+
overflow-x: auto;
|
| 254 |
+
font-family: 'Consolas', 'Monaco', 'Menlo', monospace;
|
| 255 |
+
font-size: 0.9em;
|
| 256 |
+
line-height: 1.4;
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
.example-section code {
|
| 260 |
+
font-family: 'Consolas', 'Monaco', 'Menlo', monospace;
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
+
.metadata-section {
|
| 264 |
+
margin-top: 2em;
|
| 265 |
+
padding-top: 1em;
|
| 266 |
+
border-top: 1px solid var(--border-light);
|
| 267 |
+
font-size: 0.9em;
|
| 268 |
+
color: var(--text-secondary);
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
.metadata-section h3 {
|
| 272 |
+
font-size: 1em;
|
| 273 |
+
color: var(--text-heading);
|
| 274 |
+
margin-bottom: 0.5em;
|
| 275 |
+
}
|
| 276 |
+
|
| 277 |
+
.metadata-section > div {
|
| 278 |
+
margin-bottom: 0.8em;
|
| 279 |
+
}
|
| 280 |
+
|
| 281 |
+
.source-url a {
|
| 282 |
+
color: var(--accent-blue);
|
| 283 |
+
text-decoration: none;
|
| 284 |
+
}
|
| 285 |
+
.source-url a:hover {
|
| 286 |
+
text-decoration: underline;
|
| 287 |
+
}
|
| 288 |
+
|
| 289 |
+
/* Cloze deletion styles */
|
| 290 |
+
.cloze {
|
| 291 |
+
font-weight: bold;
|
| 292 |
+
color: var(--accent-blue);
|
| 293 |
+
}
|
| 294 |
+
|
| 295 |
+
/* General utility */
|
| 296 |
+
hr {
|
| 297 |
+
border: none;
|
| 298 |
+
border-top: 1px dashed var(--border-dashed);
|
| 299 |
+
margin: 1.5em 0;
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
/* Rich text field styling */
|
| 303 |
+
.field ul, .field ol {
|
| 304 |
+
margin-left: 1.5em;
|
| 305 |
+
padding-left: 0.5em;
|
| 306 |
+
}
|
| 307 |
+
.field li {
|
| 308 |
+
margin-bottom: 0.3em;
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
/* Responsive design */
|
| 312 |
+
@media (max-width: 640px) {
|
| 313 |
+
.answer-section,
|
| 314 |
+
.explanation-section,
|
| 315 |
+
.example-section,
|
| 316 |
+
.back-extra-section {
|
| 317 |
+
padding: 1em;
|
| 318 |
+
margin: 1em 0;
|
| 319 |
+
}
|
| 320 |
+
}
|
| 321 |
+
|
| 322 |
+
/* Animations */
|
| 323 |
+
@keyframes fadeIn {
|
| 324 |
+
from { opacity: 0; }
|
| 325 |
+
to { opacity: 1; }
|
| 326 |
+
}
|
| 327 |
+
|
| 328 |
+
.card {
|
| 329 |
+
animation: fadeIn 0.3s ease-in-out;
|
| 330 |
+
}
|
| 331 |
+
"""
|
| 332 |
+
|
| 333 |
+
# --- Full Model Definitions ---
|
| 334 |
|
| 335 |
BASIC_MODEL = genanki.Model(
|
| 336 |
DEFAULT_BASIC_MODEL_ID, # Use the generated ID
|
|
|
|
| 413 |
""",
|
| 414 |
}
|
| 415 |
],
|
| 416 |
+
css=CARD_CSS,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 417 |
)
|
| 418 |
|
| 419 |
CLOZE_MODEL = genanki.Model(
|
|
|
|
| 497 |
""",
|
| 498 |
}
|
| 499 |
],
|
| 500 |
+
css=CARD_CSS,
|
| 501 |
+
model_type=1, # Cloze model type
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 502 |
)
|
| 503 |
|
| 504 |
|
ankigen_core/llm_interface.py
CHANGED
|
@@ -1,38 +1,31 @@
|
|
| 1 |
# Module for OpenAI client management and API call logic
|
| 2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
from openai import (
|
|
|
|
|
|
|
| 4 |
AsyncOpenAI,
|
| 5 |
OpenAIError,
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
) # Added OpenAIError for specific exception handling
|
| 10 |
-
import json
|
| 11 |
-
import time # Added for process_crawled_pages later, but good to have
|
| 12 |
-
from typing import List, Optional, Callable # Added List, Optional, Callable
|
| 13 |
from tenacity import (
|
| 14 |
retry,
|
|
|
|
| 15 |
stop_after_attempt,
|
| 16 |
wait_exponential,
|
| 17 |
-
retry_if_exception_type,
|
| 18 |
)
|
| 19 |
-
import asyncio # Import asyncio for gather
|
| 20 |
-
import tiktoken # Added tiktoken
|
| 21 |
|
| 22 |
-
|
| 23 |
-
from ankigen_core.
|
| 24 |
-
from ankigen_core.utils import ResponseCache
|
| 25 |
-
from ankigen_core.models import (
|
| 26 |
-
CrawledPage,
|
| 27 |
-
Card,
|
| 28 |
-
CardFront,
|
| 29 |
-
CardBack,
|
| 30 |
-
) # Added CrawledPage, Card, CardFront, CardBack
|
| 31 |
-
# We will need Pydantic models if response_format is a Pydantic model,
|
| 32 |
-
# but for now, it's a dict like {"type": "json_object"}.
|
| 33 |
-
# from ankigen_core.models import ... # Placeholder if needed later
|
| 34 |
|
| 35 |
-
|
| 36 |
|
| 37 |
|
| 38 |
class OpenAIClientManager:
|
|
@@ -121,106 +114,173 @@ class OpenAIClientManager:
|
|
| 121 |
self._client = None
|
| 122 |
|
| 123 |
|
| 124 |
-
#
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
async def structured_output_completion(
|
| 136 |
-
openai_client: AsyncOpenAI,
|
| 137 |
model: str,
|
| 138 |
-
response_format: dict, #
|
| 139 |
system_prompt: str,
|
| 140 |
user_prompt: str,
|
| 141 |
-
cache: ResponseCache,
|
| 142 |
-
):
|
| 143 |
-
"""
|
| 144 |
-
|
| 145 |
-
# Use the passed-in cache instance
|
| 146 |
-
cached_response = cache.get(f"{system_prompt}:{user_prompt}", model)
|
| 147 |
-
if cached_response is not None:
|
| 148 |
-
logger.info(f"Using cached response for model {model}")
|
| 149 |
-
return cached_response # Return cached value directly, not as a coroutine
|
| 150 |
|
| 151 |
-
|
| 152 |
-
|
|
|
|
|
|
|
| 153 |
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
response_format.get("type") == "json_object"
|
| 159 |
-
and "JSON object matching the specified schema" not in system_prompt
|
| 160 |
-
):
|
| 161 |
-
effective_system_prompt = f"{system_prompt}\nProvide your response as a JSON object matching the specified schema."
|
| 162 |
|
| 163 |
-
|
| 164 |
-
|
|
|
|
| 165 |
model=model,
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
temperature=0.7,
|
| 172 |
-
timeout=120.0, # 120 second timeout
|
| 173 |
)
|
| 174 |
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
)
|
| 179 |
-
return None # Or raise an error
|
| 180 |
-
|
| 181 |
-
first_choice = completion.choices[0]
|
| 182 |
-
if (
|
| 183 |
-
not hasattr(first_choice, "message")
|
| 184 |
-
or first_choice.message is None
|
| 185 |
-
or first_choice.message.content is None
|
| 186 |
-
):
|
| 187 |
-
logger.warning(
|
| 188 |
-
f"No message content in the first choice for OpenAI model {model}."
|
| 189 |
-
)
|
| 190 |
-
return None # Or raise an error
|
| 191 |
-
|
| 192 |
-
# Parse the JSON response
|
| 193 |
-
result = json.loads(first_choice.message.content)
|
| 194 |
-
|
| 195 |
-
# Cache the successful response using the passed-in cache instance
|
| 196 |
-
cache.set(f"{system_prompt}:{user_prompt}", model, result)
|
| 197 |
-
logger.debug(f"Successfully received and parsed response from model {model}")
|
| 198 |
return result
|
| 199 |
|
| 200 |
-
except OpenAIError as e: # More specific error handling
|
| 201 |
-
logger.error(f"OpenAI API call failed for model {model}: {e}", exc_info=True)
|
| 202 |
-
raise # Re-raise to be handled by the calling function, potentially as gr.Error
|
| 203 |
-
except json.JSONDecodeError as e:
|
| 204 |
-
# Accessing first_choice might be an issue if completion itself failed before choices
|
| 205 |
-
# However, structure assumes choices are checked before this json.loads typically
|
| 206 |
-
# For safety, check if first_choice.message.content is available
|
| 207 |
-
response_content_for_log = "<unavailable>"
|
| 208 |
-
if (
|
| 209 |
-
"first_choice" in locals()
|
| 210 |
-
and first_choice.message
|
| 211 |
-
and first_choice.message.content
|
| 212 |
-
):
|
| 213 |
-
response_content_for_log = first_choice.message.content[:500]
|
| 214 |
-
logger.error(
|
| 215 |
-
f"Failed to parse JSON response from model {model}: {e}. Response: {response_content_for_log}",
|
| 216 |
-
exc_info=True,
|
| 217 |
-
)
|
| 218 |
-
raise ValueError(
|
| 219 |
-
f"Invalid JSON response from AI model {model}."
|
| 220 |
-
) # Raise specific error
|
| 221 |
except Exception as e:
|
| 222 |
logger.error(
|
| 223 |
-
f"
|
| 224 |
exc_info=True,
|
| 225 |
)
|
| 226 |
raise # Re-raise unexpected errors
|
|
@@ -431,29 +491,24 @@ Generate a few high-quality Anki cards from this content.
|
|
| 431 |
logger.debug(
|
| 432 |
f"Attempting to generate cards for {page.url} using model {model}."
|
| 433 |
)
|
| 434 |
-
|
| 435 |
-
#
|
| 436 |
-
|
|
|
|
| 437 |
model=model,
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
],
|
| 442 |
-
response_format=response_format_param,
|
| 443 |
temperature=0.5,
|
| 444 |
-
timeout=120.0,
|
| 445 |
)
|
| 446 |
|
| 447 |
-
if
|
| 448 |
-
|
| 449 |
-
or not response_data.choices[0].message
|
| 450 |
-
or not response_data.choices[0].message.content
|
| 451 |
-
):
|
| 452 |
-
logger.error(f"Invalid or empty response from OpenAI for page {page.url}.")
|
| 453 |
return []
|
| 454 |
|
| 455 |
-
|
| 456 |
-
parsed_cards =
|
| 457 |
|
| 458 |
validated_cards: List[Card] = []
|
| 459 |
|
|
@@ -471,7 +526,7 @@ Generate a few high-quality Anki cards from this content.
|
|
| 471 |
cards_list_from_json = parsed_cards
|
| 472 |
else:
|
| 473 |
logger.error(
|
| 474 |
-
f"LLM response for {page.url} was not a list or valid dict. Response: {
|
| 475 |
)
|
| 476 |
return []
|
| 477 |
|
|
@@ -546,34 +601,9 @@ Generate a few high-quality Anki cards from this content.
|
|
| 546 |
|
| 547 |
return validated_cards
|
| 548 |
|
| 549 |
-
except json.JSONDecodeError as e:
|
| 550 |
-
# cards_json_str might not be defined if json.loads fails early, or if response_data was bad
|
| 551 |
-
raw_response_content = "<response_content_unavailable>"
|
| 552 |
-
if "cards_json_str" in locals() and cards_json_str:
|
| 553 |
-
raw_response_content = cards_json_str[:500]
|
| 554 |
-
elif (
|
| 555 |
-
"response_data" in locals()
|
| 556 |
-
and response_data
|
| 557 |
-
and response_data.choices
|
| 558 |
-
and len(response_data.choices) > 0
|
| 559 |
-
and response_data.choices[0].message
|
| 560 |
-
and response_data.choices[0].message.content
|
| 561 |
-
):
|
| 562 |
-
raw_response_content = response_data.choices[0].message.content[:500]
|
| 563 |
-
|
| 564 |
-
logger.error(
|
| 565 |
-
f"Failed to decode JSON response from OpenAI for page {page.url}: {e}. Response: {raw_response_content}...",
|
| 566 |
-
exc_info=True,
|
| 567 |
-
)
|
| 568 |
-
return []
|
| 569 |
-
except OpenAIError as e:
|
| 570 |
-
logger.error(
|
| 571 |
-
f"OpenAI API error while processing page {page.url}: {e}", exc_info=True
|
| 572 |
-
)
|
| 573 |
-
return []
|
| 574 |
except Exception as e:
|
| 575 |
logger.error(
|
| 576 |
-
f"
|
| 577 |
)
|
| 578 |
return []
|
| 579 |
|
|
|
|
| 1 |
# Module for OpenAI client management and API call logic
|
| 2 |
|
| 3 |
+
import asyncio
|
| 4 |
+
import time
|
| 5 |
+
from typing import Callable, List, Optional, TypeVar
|
| 6 |
+
|
| 7 |
+
import tiktoken
|
| 8 |
+
from agents import Agent, ModelSettings, Runner, set_default_openai_client
|
| 9 |
from openai import (
|
| 10 |
+
APIConnectionError,
|
| 11 |
+
APIStatusError,
|
| 12 |
AsyncOpenAI,
|
| 13 |
OpenAIError,
|
| 14 |
+
RateLimitError,
|
| 15 |
+
)
|
| 16 |
+
from pydantic import BaseModel
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
from tenacity import (
|
| 18 |
retry,
|
| 19 |
+
retry_if_exception_type,
|
| 20 |
stop_after_attempt,
|
| 21 |
wait_exponential,
|
|
|
|
| 22 |
)
|
|
|
|
|
|
|
| 23 |
|
| 24 |
+
from ankigen_core.logging import logger
|
| 25 |
+
from ankigen_core.models import Card, CardBack, CardFront, CrawledPage
|
| 26 |
+
from ankigen_core.utils import ResponseCache
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
+
T = TypeVar("T", bound=BaseModel)
|
| 29 |
|
| 30 |
|
| 31 |
class OpenAIClientManager:
|
|
|
|
| 114 |
self._client = None
|
| 115 |
|
| 116 |
|
| 117 |
+
# --- Agents SDK Utility ---
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
async def structured_agent_call(
|
| 121 |
+
openai_client: AsyncOpenAI,
|
| 122 |
+
model: str,
|
| 123 |
+
instructions: str,
|
| 124 |
+
user_input: str,
|
| 125 |
+
output_type: type[T],
|
| 126 |
+
cache: Optional[ResponseCache] = None,
|
| 127 |
+
cache_key: Optional[str] = None,
|
| 128 |
+
temperature: float = 0.7,
|
| 129 |
+
timeout: float = 120.0,
|
| 130 |
+
retry_attempts: int = 3,
|
| 131 |
+
) -> T:
|
| 132 |
+
"""
|
| 133 |
+
Make a single-turn structured output call using the agents SDK.
|
| 134 |
+
|
| 135 |
+
This is a lightweight wrapper for simple structured output calls,
|
| 136 |
+
not intended for complex multi-agent workflows.
|
| 137 |
+
|
| 138 |
+
Args:
|
| 139 |
+
openai_client: AsyncOpenAI client instance
|
| 140 |
+
model: Model name (e.g., "gpt-5.1", "gpt-5.1-chat-latest")
|
| 141 |
+
instructions: System instructions for the agent
|
| 142 |
+
user_input: User prompt/input
|
| 143 |
+
output_type: Pydantic model class for structured output
|
| 144 |
+
cache: Optional ResponseCache instance
|
| 145 |
+
cache_key: Cache key (required if cache is provided)
|
| 146 |
+
temperature: Model temperature (default 0.7)
|
| 147 |
+
timeout: Request timeout in seconds (default 120)
|
| 148 |
+
retry_attempts: Number of retry attempts (default 3)
|
| 149 |
+
|
| 150 |
+
Returns:
|
| 151 |
+
Instance of output_type with the structured response
|
| 152 |
+
"""
|
| 153 |
+
# 1. Check cache first
|
| 154 |
+
if cache and cache_key:
|
| 155 |
+
cached = cache.get(cache_key, model)
|
| 156 |
+
if cached is not None:
|
| 157 |
+
logger.info(f"Using cached response for model {model}")
|
| 158 |
+
# Reconstruct Pydantic model from cached dict
|
| 159 |
+
if isinstance(cached, dict):
|
| 160 |
+
return output_type.model_validate(cached)
|
| 161 |
+
return cached
|
| 162 |
+
|
| 163 |
+
# 2. Set up the OpenAI client for agents SDK
|
| 164 |
+
set_default_openai_client(openai_client, use_for_tracing=False)
|
| 165 |
+
|
| 166 |
+
# 3. Build model settings with GPT-5.1 reasoning support
|
| 167 |
+
model_settings_kwargs: dict = {"temperature": temperature}
|
| 168 |
+
|
| 169 |
+
# GPT-5.1 (not chat-latest) supports reasoning_effort
|
| 170 |
+
if model.startswith("gpt-5") and "chat-latest" not in model:
|
| 171 |
+
from openai.types.shared import Reasoning
|
| 172 |
+
|
| 173 |
+
model_settings_kwargs["reasoning"] = Reasoning(effort="none")
|
| 174 |
+
|
| 175 |
+
model_settings = ModelSettings(**model_settings_kwargs)
|
| 176 |
+
|
| 177 |
+
# 4. Create agent with structured output
|
| 178 |
+
agent = Agent(
|
| 179 |
+
name="structured_output_agent",
|
| 180 |
+
instructions=instructions,
|
| 181 |
+
model=model,
|
| 182 |
+
model_settings=model_settings,
|
| 183 |
+
output_type=output_type,
|
| 184 |
+
)
|
| 185 |
+
|
| 186 |
+
# 5. Execute with retry and timeout
|
| 187 |
+
last_error: Optional[Exception] = None
|
| 188 |
+
for attempt in range(retry_attempts):
|
| 189 |
+
try:
|
| 190 |
+
result = await asyncio.wait_for(
|
| 191 |
+
Runner.run(agent, user_input),
|
| 192 |
+
timeout=timeout,
|
| 193 |
+
)
|
| 194 |
+
|
| 195 |
+
# 6. Extract structured output
|
| 196 |
+
output = result.final_output
|
| 197 |
+
|
| 198 |
+
# 7. Cache successful result (as dict for serialization)
|
| 199 |
+
if cache and cache_key and output is not None:
|
| 200 |
+
if isinstance(output, BaseModel):
|
| 201 |
+
cache.set(cache_key, model, output.model_dump())
|
| 202 |
+
else:
|
| 203 |
+
cache.set(cache_key, model, output)
|
| 204 |
+
|
| 205 |
+
logger.debug(f"Successfully received response from model {model}")
|
| 206 |
+
return output
|
| 207 |
+
|
| 208 |
+
except asyncio.TimeoutError as e:
|
| 209 |
+
last_error = e
|
| 210 |
+
if attempt < retry_attempts - 1:
|
| 211 |
+
wait_time = 4 * (2**attempt) # Exponential backoff
|
| 212 |
+
logger.warning(
|
| 213 |
+
f"Agent timed out (attempt {attempt + 1}/{retry_attempts}), "
|
| 214 |
+
f"retrying in {wait_time}s..."
|
| 215 |
+
)
|
| 216 |
+
await asyncio.sleep(wait_time)
|
| 217 |
+
continue
|
| 218 |
+
logger.error(f"Agent timed out after {retry_attempts} attempts")
|
| 219 |
+
raise
|
| 220 |
+
except Exception as e:
|
| 221 |
+
last_error = e
|
| 222 |
+
if attempt < retry_attempts - 1:
|
| 223 |
+
wait_time = 4 * (2**attempt)
|
| 224 |
+
logger.warning(
|
| 225 |
+
f"Agent failed (attempt {attempt + 1}/{retry_attempts}): {e}, "
|
| 226 |
+
f"retrying in {wait_time}s..."
|
| 227 |
+
)
|
| 228 |
+
await asyncio.sleep(wait_time)
|
| 229 |
+
continue
|
| 230 |
+
logger.error(f"Agent failed after {retry_attempts} attempts: {e}")
|
| 231 |
+
raise
|
| 232 |
+
|
| 233 |
+
raise RuntimeError(f"Retry loop exited without result: {last_error}")
|
| 234 |
+
|
| 235 |
+
|
| 236 |
+
# Generic schema for arbitrary JSON structured outputs
|
| 237 |
+
class GenericJsonOutput(BaseModel):
|
| 238 |
+
"""Generic container for JSON output - allows any structure."""
|
| 239 |
+
|
| 240 |
+
model_config = {"extra": "allow"} # Allow arbitrary fields
|
| 241 |
+
|
| 242 |
+
|
| 243 |
async def structured_output_completion(
|
| 244 |
+
openai_client: AsyncOpenAI,
|
| 245 |
model: str,
|
| 246 |
+
response_format: dict, # Legacy parameter - kept for API compatibility
|
| 247 |
system_prompt: str,
|
| 248 |
user_prompt: str,
|
| 249 |
+
cache: ResponseCache,
|
| 250 |
+
) -> Optional[dict]:
|
| 251 |
+
"""
|
| 252 |
+
Makes an API call with structured output using agents SDK.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
|
| 254 |
+
Note: response_format parameter is ignored - the agents SDK handles
|
| 255 |
+
JSON parsing automatically. For typed outputs, use structured_agent_call() directly.
|
| 256 |
+
"""
|
| 257 |
+
cache_key = f"{system_prompt}:{user_prompt}"
|
| 258 |
|
| 259 |
+
# Ensure system_prompt includes JSON instruction
|
| 260 |
+
effective_system_prompt = system_prompt
|
| 261 |
+
if "JSON object matching the specified schema" not in system_prompt:
|
| 262 |
+
effective_system_prompt = f"{system_prompt}\nProvide your response as a JSON object matching the specified schema."
|
|
|
|
|
|
|
|
|
|
|
|
|
| 263 |
|
| 264 |
+
try:
|
| 265 |
+
result = await structured_agent_call(
|
| 266 |
+
openai_client=openai_client,
|
| 267 |
model=model,
|
| 268 |
+
instructions=effective_system_prompt.strip(),
|
| 269 |
+
user_input=user_prompt.strip(),
|
| 270 |
+
output_type=GenericJsonOutput,
|
| 271 |
+
cache=cache,
|
| 272 |
+
cache_key=cache_key,
|
| 273 |
+
temperature=0.7,
|
|
|
|
| 274 |
)
|
| 275 |
|
| 276 |
+
# Convert Pydantic model back to dict for backward compatibility
|
| 277 |
+
if isinstance(result, BaseModel):
|
| 278 |
+
return result.model_dump()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
return result
|
| 280 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 281 |
except Exception as e:
|
| 282 |
logger.error(
|
| 283 |
+
f"structured_output_completion failed for model {model}: {e}",
|
| 284 |
exc_info=True,
|
| 285 |
)
|
| 286 |
raise # Re-raise unexpected errors
|
|
|
|
| 491 |
logger.debug(
|
| 492 |
f"Attempting to generate cards for {page.url} using model {model}."
|
| 493 |
)
|
| 494 |
+
|
| 495 |
+
# Use agents SDK for structured output
|
| 496 |
+
result = await structured_agent_call(
|
| 497 |
+
openai_client=openai_client,
|
| 498 |
model=model,
|
| 499 |
+
instructions=system_prompt,
|
| 500 |
+
user_input=user_prompt,
|
| 501 |
+
output_type=GenericJsonOutput, # Flexible schema for card generation
|
|
|
|
|
|
|
| 502 |
temperature=0.5,
|
| 503 |
+
timeout=120.0,
|
| 504 |
)
|
| 505 |
|
| 506 |
+
if result is None:
|
| 507 |
+
logger.error(f"Invalid or empty response from agent for page {page.url}.")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 508 |
return []
|
| 509 |
|
| 510 |
+
# Convert Pydantic model to dict for processing
|
| 511 |
+
parsed_cards = result.model_dump() if isinstance(result, BaseModel) else result
|
| 512 |
|
| 513 |
validated_cards: List[Card] = []
|
| 514 |
|
|
|
|
| 526 |
cards_list_from_json = parsed_cards
|
| 527 |
else:
|
| 528 |
logger.error(
|
| 529 |
+
f"LLM response for {page.url} was not a list or valid dict. Response: {str(parsed_cards)[:200]}..."
|
| 530 |
)
|
| 531 |
return []
|
| 532 |
|
|
|
|
| 601 |
|
| 602 |
return validated_cards
|
| 603 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 604 |
except Exception as e:
|
| 605 |
logger.error(
|
| 606 |
+
f"Error processing page {page.url} with agents SDK: {e}", exc_info=True
|
| 607 |
)
|
| 608 |
return []
|
| 609 |
|
pyproject.toml
CHANGED
|
@@ -12,12 +12,12 @@ authors = [
|
|
| 12 |
readme = "README.md"
|
| 13 |
requires-python = ">=3.12"
|
| 14 |
dependencies = [
|
| 15 |
-
"openai>=
|
| 16 |
-
"openai-agents>=0.
|
| 17 |
"gradio>=5.49.1",
|
| 18 |
"tenacity>=9.1.2",
|
| 19 |
"genanki>=0.13.1",
|
| 20 |
-
"pydantic=
|
| 21 |
"pandas>=2.3.2",
|
| 22 |
"beautifulsoup4==4.13.5",
|
| 23 |
"lxml>=6.0.2",
|
|
|
|
| 12 |
readme = "README.md"
|
| 13 |
requires-python = ">=3.12"
|
| 14 |
dependencies = [
|
| 15 |
+
"openai>=2.8.0",
|
| 16 |
+
"openai-agents>=0.6.1",
|
| 17 |
"gradio>=5.49.1",
|
| 18 |
"tenacity>=9.1.2",
|
| 19 |
"genanki>=0.13.1",
|
| 20 |
+
"pydantic>=2.12.3",
|
| 21 |
"pandas>=2.3.2",
|
| 22 |
"beautifulsoup4==4.13.5",
|
| 23 |
"lxml>=6.0.2",
|
requirements.txt
CHANGED
|
@@ -89,9 +89,9 @@ fsspec==2025.5.1
|
|
| 89 |
# huggingface-hub
|
| 90 |
genanki==0.13.1
|
| 91 |
# via ankigen (pyproject.toml)
|
| 92 |
-
gradio==
|
| 93 |
# via ankigen (pyproject.toml)
|
| 94 |
-
gradio-client==
|
| 95 |
# via gradio
|
| 96 |
griffe==1.7.3
|
| 97 |
# via openai-agents
|
|
@@ -183,11 +183,11 @@ numpy==2.3.1
|
|
| 183 |
# via
|
| 184 |
# gradio
|
| 185 |
# pandas
|
| 186 |
-
openai==
|
| 187 |
# via
|
| 188 |
# ankigen (pyproject.toml)
|
| 189 |
# openai-agents
|
| 190 |
-
openai-agents==0.
|
| 191 |
# via ankigen (pyproject.toml)
|
| 192 |
openapi-core==0.19.5
|
| 193 |
# via fastmcp
|
|
@@ -226,7 +226,7 @@ py-key-value-shared==0.2.8
|
|
| 226 |
# via py-key-value-aio
|
| 227 |
pycparser==2.23
|
| 228 |
# via cffi
|
| 229 |
-
pydantic==2.
|
| 230 |
# via
|
| 231 |
# ankigen (pyproject.toml)
|
| 232 |
# fastapi
|
|
@@ -237,7 +237,7 @@ pydantic==2.11.9
|
|
| 237 |
# openai-agents
|
| 238 |
# openapi-pydantic
|
| 239 |
# pydantic-settings
|
| 240 |
-
pydantic-core==2.
|
| 241 |
# via pydantic
|
| 242 |
pydantic-settings==2.10.1
|
| 243 |
# via mcp
|
|
@@ -294,9 +294,7 @@ rpds-py==0.26.0
|
|
| 294 |
# via
|
| 295 |
# jsonschema
|
| 296 |
# referencing
|
| 297 |
-
|
| 298 |
-
# via gradio
|
| 299 |
-
safehttpx==0.1.6
|
| 300 |
# via gradio
|
| 301 |
secretstorage==3.4.0
|
| 302 |
# via keyring
|
|
@@ -355,7 +353,7 @@ typing-extensions==4.15.0
|
|
| 355 |
# starlette
|
| 356 |
# typer
|
| 357 |
# typing-inspection
|
| 358 |
-
typing-inspection==0.4.
|
| 359 |
# via
|
| 360 |
# pydantic
|
| 361 |
# pydantic-settings
|
|
@@ -370,8 +368,6 @@ uvicorn==0.34.3
|
|
| 370 |
# gradio
|
| 371 |
# mcp
|
| 372 |
websockets==15.0.1
|
| 373 |
-
# via
|
| 374 |
-
# fastmcp
|
| 375 |
-
# gradio-client
|
| 376 |
werkzeug==3.1.1
|
| 377 |
# via openapi-core
|
|
|
|
| 89 |
# huggingface-hub
|
| 90 |
genanki==0.13.1
|
| 91 |
# via ankigen (pyproject.toml)
|
| 92 |
+
gradio==6.0.2
|
| 93 |
# via ankigen (pyproject.toml)
|
| 94 |
+
gradio-client==2.0.1
|
| 95 |
# via gradio
|
| 96 |
griffe==1.7.3
|
| 97 |
# via openai-agents
|
|
|
|
| 183 |
# via
|
| 184 |
# gradio
|
| 185 |
# pandas
|
| 186 |
+
openai==2.8.1
|
| 187 |
# via
|
| 188 |
# ankigen (pyproject.toml)
|
| 189 |
# openai-agents
|
| 190 |
+
openai-agents==0.6.1
|
| 191 |
# via ankigen (pyproject.toml)
|
| 192 |
openapi-core==0.19.5
|
| 193 |
# via fastmcp
|
|
|
|
| 226 |
# via py-key-value-aio
|
| 227 |
pycparser==2.23
|
| 228 |
# via cffi
|
| 229 |
+
pydantic==2.12.4
|
| 230 |
# via
|
| 231 |
# ankigen (pyproject.toml)
|
| 232 |
# fastapi
|
|
|
|
| 237 |
# openai-agents
|
| 238 |
# openapi-pydantic
|
| 239 |
# pydantic-settings
|
| 240 |
+
pydantic-core==2.41.5
|
| 241 |
# via pydantic
|
| 242 |
pydantic-settings==2.10.1
|
| 243 |
# via mcp
|
|
|
|
| 294 |
# via
|
| 295 |
# jsonschema
|
| 296 |
# referencing
|
| 297 |
+
safehttpx==0.1.7
|
|
|
|
|
|
|
| 298 |
# via gradio
|
| 299 |
secretstorage==3.4.0
|
| 300 |
# via keyring
|
|
|
|
| 353 |
# starlette
|
| 354 |
# typer
|
| 355 |
# typing-inspection
|
| 356 |
+
typing-inspection==0.4.2
|
| 357 |
# via
|
| 358 |
# pydantic
|
| 359 |
# pydantic-settings
|
|
|
|
| 368 |
# gradio
|
| 369 |
# mcp
|
| 370 |
websockets==15.0.1
|
| 371 |
+
# via fastmcp
|
|
|
|
|
|
|
| 372 |
werkzeug==3.1.1
|
| 373 |
# via openapi-core
|
uv.lock
CHANGED
|
@@ -61,11 +61,11 @@ requires-dist = [
|
|
| 61 |
{ name = "genanki", specifier = ">=0.13.1" },
|
| 62 |
{ name = "gradio", specifier = ">=5.49.1" },
|
| 63 |
{ name = "lxml", specifier = ">=6.0.2" },
|
| 64 |
-
{ name = "openai", specifier = ">=
|
| 65 |
-
{ name = "openai-agents", specifier = ">=0.
|
| 66 |
{ name = "pandas", specifier = ">=2.3.2" },
|
| 67 |
{ name = "pre-commit", marker = "extra == 'dev'", specifier = ">=4.3.0" },
|
| 68 |
-
{ name = "pydantic", specifier = "=
|
| 69 |
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.4.2" },
|
| 70 |
{ name = "pytest-anyio", marker = "extra == 'dev'", specifier = ">=0.0.0" },
|
| 71 |
{ name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=7.0.0" },
|
|
@@ -734,7 +734,7 @@ wheels = [
|
|
| 734 |
|
| 735 |
[[package]]
|
| 736 |
name = "gradio"
|
| 737 |
-
version = "
|
| 738 |
source = { registry = "https://pypi.org/simple" }
|
| 739 |
dependencies = [
|
| 740 |
{ name = "aiofiles" },
|
|
@@ -758,7 +758,6 @@ dependencies = [
|
|
| 758 |
{ name = "pydub" },
|
| 759 |
{ name = "python-multipart" },
|
| 760 |
{ name = "pyyaml" },
|
| 761 |
-
{ name = "ruff" },
|
| 762 |
{ name = "safehttpx" },
|
| 763 |
{ name = "semantic-version" },
|
| 764 |
{ name = "starlette" },
|
|
@@ -767,14 +766,14 @@ dependencies = [
|
|
| 767 |
{ name = "typing-extensions" },
|
| 768 |
{ name = "uvicorn" },
|
| 769 |
]
|
| 770 |
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
| 771 |
wheels = [
|
| 772 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 773 |
]
|
| 774 |
|
| 775 |
[[package]]
|
| 776 |
name = "gradio-client"
|
| 777 |
-
version = "
|
| 778 |
source = { registry = "https://pypi.org/simple" }
|
| 779 |
dependencies = [
|
| 780 |
{ name = "fsspec" },
|
|
@@ -782,11 +781,10 @@ dependencies = [
|
|
| 782 |
{ name = "huggingface-hub" },
|
| 783 |
{ name = "packaging" },
|
| 784 |
{ name = "typing-extensions" },
|
| 785 |
-
{ name = "websockets" },
|
| 786 |
]
|
| 787 |
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
| 788 |
wheels = [
|
| 789 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 790 |
]
|
| 791 |
|
| 792 |
[[package]]
|
|
@@ -1351,7 +1349,7 @@ wheels = [
|
|
| 1351 |
|
| 1352 |
[[package]]
|
| 1353 |
name = "openai"
|
| 1354 |
-
version = "
|
| 1355 |
source = { registry = "https://pypi.org/simple" }
|
| 1356 |
dependencies = [
|
| 1357 |
{ name = "anyio" },
|
|
@@ -1363,14 +1361,14 @@ dependencies = [
|
|
| 1363 |
{ name = "tqdm" },
|
| 1364 |
{ name = "typing-extensions" },
|
| 1365 |
]
|
| 1366 |
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
| 1367 |
wheels = [
|
| 1368 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1369 |
]
|
| 1370 |
|
| 1371 |
[[package]]
|
| 1372 |
name = "openai-agents"
|
| 1373 |
-
version = "0.
|
| 1374 |
source = { registry = "https://pypi.org/simple" }
|
| 1375 |
dependencies = [
|
| 1376 |
{ name = "griffe" },
|
|
@@ -1381,9 +1379,9 @@ dependencies = [
|
|
| 1381 |
{ name = "types-requests" },
|
| 1382 |
{ name = "typing-extensions" },
|
| 1383 |
]
|
| 1384 |
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
| 1385 |
wheels = [
|
| 1386 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1387 |
]
|
| 1388 |
|
| 1389 |
[[package]]
|
|
@@ -1688,7 +1686,7 @@ wheels = [
|
|
| 1688 |
|
| 1689 |
[[package]]
|
| 1690 |
name = "pydantic"
|
| 1691 |
-
version = "2.
|
| 1692 |
source = { registry = "https://pypi.org/simple" }
|
| 1693 |
dependencies = [
|
| 1694 |
{ name = "annotated-types" },
|
|
@@ -1696,9 +1694,9 @@ dependencies = [
|
|
| 1696 |
{ name = "typing-extensions" },
|
| 1697 |
{ name = "typing-inspection" },
|
| 1698 |
]
|
| 1699 |
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
| 1700 |
wheels = [
|
| 1701 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1702 |
]
|
| 1703 |
|
| 1704 |
[package.optional-dependencies]
|
|
@@ -1708,44 +1706,73 @@ email = [
|
|
| 1708 |
|
| 1709 |
[[package]]
|
| 1710 |
name = "pydantic-core"
|
| 1711 |
-
version = "2.
|
| 1712 |
source = { registry = "https://pypi.org/simple" }
|
| 1713 |
dependencies = [
|
| 1714 |
{ name = "typing-extensions" },
|
| 1715 |
]
|
| 1716 |
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
| 1717 |
-
wheels = [
|
| 1718 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1719 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1720 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1721 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1722 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1723 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1724 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1725 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1726 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1727 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1728 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1729 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1730 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1731 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1732 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1733 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1734 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1735 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1736 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1737 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1738 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1739 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1740 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1741 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1742 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1743 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1744 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1745 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1746 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1747 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1748 |
-
{ url = "https://files.pythonhosted.org/packages/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1749 |
]
|
| 1750 |
|
| 1751 |
[[package]]
|
|
@@ -2166,14 +2193,14 @@ wheels = [
|
|
| 2166 |
|
| 2167 |
[[package]]
|
| 2168 |
name = "safehttpx"
|
| 2169 |
-
version = "0.1.
|
| 2170 |
source = { registry = "https://pypi.org/simple" }
|
| 2171 |
dependencies = [
|
| 2172 |
{ name = "httpx" },
|
| 2173 |
]
|
| 2174 |
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
| 2175 |
wheels = [
|
| 2176 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 2177 |
]
|
| 2178 |
|
| 2179 |
[[package]]
|
|
@@ -2351,14 +2378,14 @@ wheels = [
|
|
| 2351 |
|
| 2352 |
[[package]]
|
| 2353 |
name = "typing-inspection"
|
| 2354 |
-
version = "0.4.
|
| 2355 |
source = { registry = "https://pypi.org/simple" }
|
| 2356 |
dependencies = [
|
| 2357 |
{ name = "typing-extensions" },
|
| 2358 |
]
|
| 2359 |
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
| 2360 |
wheels = [
|
| 2361 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 2362 |
]
|
| 2363 |
|
| 2364 |
[[package]]
|
|
|
|
| 61 |
{ name = "genanki", specifier = ">=0.13.1" },
|
| 62 |
{ name = "gradio", specifier = ">=5.49.1" },
|
| 63 |
{ name = "lxml", specifier = ">=6.0.2" },
|
| 64 |
+
{ name = "openai", specifier = ">=2.8.0" },
|
| 65 |
+
{ name = "openai-agents", specifier = ">=0.6.1" },
|
| 66 |
{ name = "pandas", specifier = ">=2.3.2" },
|
| 67 |
{ name = "pre-commit", marker = "extra == 'dev'", specifier = ">=4.3.0" },
|
| 68 |
+
{ name = "pydantic", specifier = ">=2.12.3" },
|
| 69 |
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.4.2" },
|
| 70 |
{ name = "pytest-anyio", marker = "extra == 'dev'", specifier = ">=0.0.0" },
|
| 71 |
{ name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=7.0.0" },
|
|
|
|
| 734 |
|
| 735 |
[[package]]
|
| 736 |
name = "gradio"
|
| 737 |
+
version = "6.0.2"
|
| 738 |
source = { registry = "https://pypi.org/simple" }
|
| 739 |
dependencies = [
|
| 740 |
{ name = "aiofiles" },
|
|
|
|
| 758 |
{ name = "pydub" },
|
| 759 |
{ name = "python-multipart" },
|
| 760 |
{ name = "pyyaml" },
|
|
|
|
| 761 |
{ name = "safehttpx" },
|
| 762 |
{ name = "semantic-version" },
|
| 763 |
{ name = "starlette" },
|
|
|
|
| 766 |
{ name = "typing-extensions" },
|
| 767 |
{ name = "uvicorn" },
|
| 768 |
]
|
| 769 |
+
sdist = { url = "https://files.pythonhosted.org/packages/b8/e8/66612eabc43a792b09ea8080d5581644128823b4f20185881f86a042e470/gradio-6.0.2.tar.gz", hash = "sha256:e3bb128fd9247a49820048cfb8f5b677b59a0da24f6d81bc990ca1a20eb6d1fb", size = 36452504, upload-time = "2025-12-02T01:57:48.397Z" }
|
| 770 |
wheels = [
|
| 771 |
+
{ url = "https://files.pythonhosted.org/packages/1c/b0/d4bb7a5c9c2e26a952da06f046c6411fb8678003a0b9c7ed379941aff21f/gradio-6.0.2-py3-none-any.whl", hash = "sha256:bcb8b9d147b313c958f811977527415cfd7871ee9547ccd92ef1911970c49a2c", size = 21560841, upload-time = "2025-12-02T01:57:45.471Z" },
|
| 772 |
]
|
| 773 |
|
| 774 |
[[package]]
|
| 775 |
name = "gradio-client"
|
| 776 |
+
version = "2.0.1"
|
| 777 |
source = { registry = "https://pypi.org/simple" }
|
| 778 |
dependencies = [
|
| 779 |
{ name = "fsspec" },
|
|
|
|
| 781 |
{ name = "huggingface-hub" },
|
| 782 |
{ name = "packaging" },
|
| 783 |
{ name = "typing-extensions" },
|
|
|
|
| 784 |
]
|
| 785 |
+
sdist = { url = "https://files.pythonhosted.org/packages/4e/cc/b0f04b1c9bf79c7ae9840b9945f5fbd93355719684f83032837695ab1eaf/gradio_client-2.0.1.tar.gz", hash = "sha256:087eb50652370747c0ce66cd0ae79ecb49f9682188d5348e279d44602cbc2814", size = 54792, upload-time = "2025-12-02T01:57:58.685Z" }
|
| 786 |
wheels = [
|
| 787 |
+
{ url = "https://files.pythonhosted.org/packages/1b/11/d680ecf4073bd1cacfe9dea57fa95660e4ea2d1fff3125dbaaa902cc9095/gradio_client-2.0.1-py3-none-any.whl", hash = "sha256:6322eecb5963a07703306c0b048bb98518063d05ca99a65fe384417188af8c63", size = 55439, upload-time = "2025-12-02T01:57:57.551Z" },
|
| 788 |
]
|
| 789 |
|
| 790 |
[[package]]
|
|
|
|
| 1349 |
|
| 1350 |
[[package]]
|
| 1351 |
name = "openai"
|
| 1352 |
+
version = "2.8.1"
|
| 1353 |
source = { registry = "https://pypi.org/simple" }
|
| 1354 |
dependencies = [
|
| 1355 |
{ name = "anyio" },
|
|
|
|
| 1361 |
{ name = "tqdm" },
|
| 1362 |
{ name = "typing-extensions" },
|
| 1363 |
]
|
| 1364 |
+
sdist = { url = "https://files.pythonhosted.org/packages/d5/e4/42591e356f1d53c568418dc7e30dcda7be31dd5a4d570bca22acb0525862/openai-2.8.1.tar.gz", hash = "sha256:cb1b79eef6e809f6da326a7ef6038719e35aa944c42d081807bfa1be8060f15f", size = 602490, upload-time = "2025-11-17T22:39:59.549Z" }
|
| 1365 |
wheels = [
|
| 1366 |
+
{ url = "https://files.pythonhosted.org/packages/55/4f/dbc0c124c40cb390508a82770fb9f6e3ed162560181a85089191a851c59a/openai-2.8.1-py3-none-any.whl", hash = "sha256:c6c3b5a04994734386e8dad3c00a393f56d3b68a27cd2e8acae91a59e4122463", size = 1022688, upload-time = "2025-11-17T22:39:57.675Z" },
|
| 1367 |
]
|
| 1368 |
|
| 1369 |
[[package]]
|
| 1370 |
name = "openai-agents"
|
| 1371 |
+
version = "0.6.1"
|
| 1372 |
source = { registry = "https://pypi.org/simple" }
|
| 1373 |
dependencies = [
|
| 1374 |
{ name = "griffe" },
|
|
|
|
| 1379 |
{ name = "types-requests" },
|
| 1380 |
{ name = "typing-extensions" },
|
| 1381 |
]
|
| 1382 |
+
sdist = { url = "https://files.pythonhosted.org/packages/88/dc/7323c8d96211f252a2ebb288782a8f40bef6b472878eeebb59b3097a5708/openai_agents-0.6.1.tar.gz", hash = "sha256:067d2b66669c390c840effeb02d80939b4ac4a4db53e9735b74895a6d916b840", size = 2011463, upload-time = "2025-11-20T01:17:07.941Z" }
|
| 1383 |
wheels = [
|
| 1384 |
+
{ url = "https://files.pythonhosted.org/packages/11/53/d8076306f324992c79e9b2ee597f2ce863f0ac5d1fd24e6ad88f2a4dcbc0/openai_agents-0.6.1-py3-none-any.whl", hash = "sha256:7bde01c8d2fd723b0c72c9b207dcfeb12a8d211078f5d259945fb163a6f52b89", size = 237609, upload-time = "2025-11-20T01:17:06.454Z" },
|
| 1385 |
]
|
| 1386 |
|
| 1387 |
[[package]]
|
|
|
|
| 1686 |
|
| 1687 |
[[package]]
|
| 1688 |
name = "pydantic"
|
| 1689 |
+
version = "2.12.4"
|
| 1690 |
source = { registry = "https://pypi.org/simple" }
|
| 1691 |
dependencies = [
|
| 1692 |
{ name = "annotated-types" },
|
|
|
|
| 1694 |
{ name = "typing-extensions" },
|
| 1695 |
{ name = "typing-inspection" },
|
| 1696 |
]
|
| 1697 |
+
sdist = { url = "https://files.pythonhosted.org/packages/96/ad/a17bc283d7d81837c061c49e3eaa27a45991759a1b7eae1031921c6bd924/pydantic-2.12.4.tar.gz", hash = "sha256:0f8cb9555000a4b5b617f66bfd2566264c4984b27589d3b845685983e8ea85ac", size = 821038, upload-time = "2025-11-05T10:50:08.59Z" }
|
| 1698 |
wheels = [
|
| 1699 |
+
{ url = "https://files.pythonhosted.org/packages/82/2f/e68750da9b04856e2a7ec56fc6f034a5a79775e9b9a81882252789873798/pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e", size = 463400, upload-time = "2025-11-05T10:50:06.732Z" },
|
| 1700 |
]
|
| 1701 |
|
| 1702 |
[package.optional-dependencies]
|
|
|
|
| 1706 |
|
| 1707 |
[[package]]
|
| 1708 |
name = "pydantic-core"
|
| 1709 |
+
version = "2.41.5"
|
| 1710 |
source = { registry = "https://pypi.org/simple" }
|
| 1711 |
dependencies = [
|
| 1712 |
{ name = "typing-extensions" },
|
| 1713 |
]
|
| 1714 |
+
sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" }
|
| 1715 |
+
wheels = [
|
| 1716 |
+
{ url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" },
|
| 1717 |
+
{ url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" },
|
| 1718 |
+
{ url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" },
|
| 1719 |
+
{ url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" },
|
| 1720 |
+
{ url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" },
|
| 1721 |
+
{ url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" },
|
| 1722 |
+
{ url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" },
|
| 1723 |
+
{ url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" },
|
| 1724 |
+
{ url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" },
|
| 1725 |
+
{ url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" },
|
| 1726 |
+
{ url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" },
|
| 1727 |
+
{ url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" },
|
| 1728 |
+
{ url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" },
|
| 1729 |
+
{ url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" },
|
| 1730 |
+
{ url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" },
|
| 1731 |
+
{ url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" },
|
| 1732 |
+
{ url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" },
|
| 1733 |
+
{ url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" },
|
| 1734 |
+
{ url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" },
|
| 1735 |
+
{ url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" },
|
| 1736 |
+
{ url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" },
|
| 1737 |
+
{ url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" },
|
| 1738 |
+
{ url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" },
|
| 1739 |
+
{ url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" },
|
| 1740 |
+
{ url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" },
|
| 1741 |
+
{ url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" },
|
| 1742 |
+
{ url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" },
|
| 1743 |
+
{ url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" },
|
| 1744 |
+
{ url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" },
|
| 1745 |
+
{ url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" },
|
| 1746 |
+
{ url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" },
|
| 1747 |
+
{ url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" },
|
| 1748 |
+
{ url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" },
|
| 1749 |
+
{ url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" },
|
| 1750 |
+
{ url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" },
|
| 1751 |
+
{ url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" },
|
| 1752 |
+
{ url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" },
|
| 1753 |
+
{ url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" },
|
| 1754 |
+
{ url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" },
|
| 1755 |
+
{ url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" },
|
| 1756 |
+
{ url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" },
|
| 1757 |
+
{ url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" },
|
| 1758 |
+
{ url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" },
|
| 1759 |
+
{ url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" },
|
| 1760 |
+
{ url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" },
|
| 1761 |
+
{ url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" },
|
| 1762 |
+
{ url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" },
|
| 1763 |
+
{ url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" },
|
| 1764 |
+
{ url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" },
|
| 1765 |
+
{ url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" },
|
| 1766 |
+
{ url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" },
|
| 1767 |
+
{ url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" },
|
| 1768 |
+
{ url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" },
|
| 1769 |
+
{ url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" },
|
| 1770 |
+
{ url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" },
|
| 1771 |
+
{ url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" },
|
| 1772 |
+
{ url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" },
|
| 1773 |
+
{ url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" },
|
| 1774 |
+
{ url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" },
|
| 1775 |
+
{ url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" },
|
| 1776 |
]
|
| 1777 |
|
| 1778 |
[[package]]
|
|
|
|
| 2193 |
|
| 2194 |
[[package]]
|
| 2195 |
name = "safehttpx"
|
| 2196 |
+
version = "0.1.7"
|
| 2197 |
source = { registry = "https://pypi.org/simple" }
|
| 2198 |
dependencies = [
|
| 2199 |
{ name = "httpx" },
|
| 2200 |
]
|
| 2201 |
+
sdist = { url = "https://files.pythonhosted.org/packages/89/d1/4282284d9cf1ee873607a46442da977fc3c985059315ab23610be31d5885/safehttpx-0.1.7.tar.gz", hash = "sha256:db201c0978c41eddb8bb480f3eee59dd67304fdd91646035e9d9a720049a9d23", size = 10385, upload-time = "2025-10-24T18:30:09.783Z" }
|
| 2202 |
wheels = [
|
| 2203 |
+
{ url = "https://files.pythonhosted.org/packages/2e/a3/0f0b7d78e2f1eb9e8e1afbff1d2bff8d60144aee17aca51c065b516743dd/safehttpx-0.1.7-py3-none-any.whl", hash = "sha256:c4f4a162db6993464d7ca3d7cc4af0ffc6515a606dfd220b9f82c6945d869cde", size = 8959, upload-time = "2025-10-24T18:30:08.733Z" },
|
| 2204 |
]
|
| 2205 |
|
| 2206 |
[[package]]
|
|
|
|
| 2378 |
|
| 2379 |
[[package]]
|
| 2380 |
name = "typing-inspection"
|
| 2381 |
+
version = "0.4.2"
|
| 2382 |
source = { registry = "https://pypi.org/simple" }
|
| 2383 |
dependencies = [
|
| 2384 |
{ name = "typing-extensions" },
|
| 2385 |
]
|
| 2386 |
+
sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
|
| 2387 |
wheels = [
|
| 2388 |
+
{ url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
|
| 2389 |
]
|
| 2390 |
|
| 2391 |
[[package]]
|