diff --git a/src/scribae/brief.py b/src/scribae/brief.py index e21c4dd..651f74c 100644 --- a/src/scribae/brief.py +++ b/src/scribae/brief.py @@ -14,7 +14,7 @@ from pydantic_ai.settings import ModelSettings from .io_utils import NoteDetails, load_note -from .llm import LLM_OUTPUT_RETRIES, OpenAISettings, make_model +from .llm import LLM_OUTPUT_RETRIES, LLM_TIMEOUT_SECONDS, OpenAISettings, make_model from .project import ProjectConfig from .prompts import SYSTEM_PROMPT, PromptBundle, build_prompt_bundle @@ -33,9 +33,6 @@ "save_prompt_artifacts", ] -LLM_TIMEOUT_SECONDS = 300.0 - - class BriefingError(Exception): """Raised when a brief cannot be generated.""" diff --git a/src/scribae/idea.py b/src/scribae/idea.py index b0baa51..89d6776 100644 --- a/src/scribae/idea.py +++ b/src/scribae/idea.py @@ -14,12 +14,14 @@ from pydantic_ai.settings import ModelSettings from .io_utils import NoteDetails, load_note -from .llm import DEFAULT_MODEL_NAME, LLM_OUTPUT_RETRIES, OpenAISettings, make_model +from .llm import ( + LLM_OUTPUT_RETRIES, + LLM_TIMEOUT_SECONDS, + OpenAISettings, + make_model, +) from .project import ProjectConfig -DEFAULT_IDEA_MODEL = DEFAULT_MODEL_NAME -IDEA_TIMEOUT_SECONDS = 300.0 - class IdeaError(Exception): """Raised when ideas cannot be generated.""" @@ -138,7 +140,7 @@ def generate_ideas( reporter: Reporter = None, settings: OpenAISettings | None = None, agent: Agent[None, IdeaList] | None = None, - timeout_seconds: float = IDEA_TIMEOUT_SECONDS, + timeout_seconds: float = LLM_TIMEOUT_SECONDS, ) -> IdeaList: """Run the LLM call and return validated ideas.""" @@ -290,7 +292,6 @@ def _report(reporter: Reporter, message: str) -> None: __all__ = [ - "DEFAULT_IDEA_MODEL", "Idea", "IdeaContext", "IdeaError", diff --git a/src/scribae/idea_cli.py b/src/scribae/idea_cli.py index 1fb247c..6ca3c3f 100644 --- a/src/scribae/idea_cli.py +++ b/src/scribae/idea_cli.py @@ -4,7 +4,8 @@ import typer -from .idea import DEFAULT_IDEA_MODEL, IdeaError, generate_ideas, prepare_context, render_json, save_prompt_artifacts +from .idea import IdeaError, generate_ideas, prepare_context, render_json, save_prompt_artifacts +from .llm import DEFAULT_MODEL_NAME from .project import default_project, load_project @@ -50,7 +51,7 @@ def idea_command( help="Project name used to load projects/.yaml for prompt context.", ), model: str = typer.Option( # noqa: B008 - DEFAULT_IDEA_MODEL, + DEFAULT_MODEL_NAME, "--model", "-m", help="OpenAI/Ollama model name to request.", diff --git a/src/scribae/llm.py b/src/scribae/llm.py index f19509e..7c9cf39 100644 --- a/src/scribae/llm.py +++ b/src/scribae/llm.py @@ -7,10 +7,11 @@ from pydantic_ai.providers.openai import OpenAIProvider from pydantic_ai.settings import ModelSettings -DEFAULT_MODEL_NAME = "mistral-nemo" +DEFAULT_MODEL_NAME = "ministral-3:8b" DEFAULT_BASE_URL = "http://localhost:11434/v1" DEFAULT_API_KEY = "no-key" LLM_OUTPUT_RETRIES = 2 +LLM_TIMEOUT_SECONDS = 300.0 @dataclass(frozen=True) @@ -48,5 +49,6 @@ def make_model(model_name: str, *, model_settings: ModelSettings, "DEFAULT_BASE_URL", "DEFAULT_API_KEY", "LLM_OUTPUT_RETRIES", + "LLM_TIMEOUT_SECONDS", "make_model", ] diff --git a/src/scribae/meta.py b/src/scribae/meta.py index 2ca5424..95ba0e1 100644 --- a/src/scribae/meta.py +++ b/src/scribae/meta.py @@ -16,12 +16,9 @@ from pydantic_ai.settings import ModelSettings from .brief import SeoBrief -from .llm import LLM_OUTPUT_RETRIES, OpenAISettings, make_model +from .llm import LLM_OUTPUT_RETRIES, LLM_TIMEOUT_SECONDS, OpenAISettings, make_model from .project import ProjectConfig -DEFAULT_META_MODEL = "mistral-nemo" -META_TIMEOUT_SECONDS = 300.0 - Reporter = Callable[[str], None] | None @@ -278,7 +275,7 @@ def generate_metadata( reporter: Reporter = None, agent: Agent[None, ArticleMeta] | None = None, prompts: PromptBundle | None = None, - timeout_seconds: float = META_TIMEOUT_SECONDS, + timeout_seconds: float = LLM_TIMEOUT_SECONDS, force_llm_on_missing: bool = True, ) -> ArticleMeta: """Generate final article metadata, calling the LLM when needed.""" @@ -670,7 +667,6 @@ def _report(reporter: Reporter, message: str) -> None: __all__ = [ "ArticleMeta", "BodyDocument", - "DEFAULT_META_MODEL", "MetaBriefError", "MetaContext", "MetaError", diff --git a/src/scribae/meta_cli.py b/src/scribae/meta_cli.py index 2f7d3fb..a27a1ed 100644 --- a/src/scribae/meta_cli.py +++ b/src/scribae/meta_cli.py @@ -4,8 +4,8 @@ import typer +from .llm import DEFAULT_MODEL_NAME from .meta import ( - DEFAULT_META_MODEL, ArticleMeta, MetaBriefError, MetaError, @@ -46,7 +46,7 @@ def meta_command( help="Project name used to load projects/.yaml for prompt context.", ), model: str = typer.Option( # noqa: B008 - DEFAULT_META_MODEL, + DEFAULT_MODEL_NAME, "--model", "-m", help="OpenAI/Ollama model name to request.", diff --git a/src/scribae/translate/postedit.py b/src/scribae/translate/postedit.py index b90e038..65fbbef 100644 --- a/src/scribae/translate/postedit.py +++ b/src/scribae/translate/postedit.py @@ -10,7 +10,7 @@ from pydantic_ai import Agent, NativeOutput, UnexpectedModelBehavior from pydantic_ai.settings import ModelSettings -from scribae.llm import LLM_OUTPUT_RETRIES, OpenAISettings, make_model +from scribae.llm import DEFAULT_MODEL_NAME, LLM_OUTPUT_RETRIES, OpenAISettings, make_model from .markdown_segmenter import ProtectedText @@ -43,7 +43,7 @@ def __init__( self, agent: Agent[None, str] | None = None, *, - model_name: str = "mistral-nemo", + model_name: str = DEFAULT_MODEL_NAME, temperature: float = 0.2, create_agent: bool = True, max_chars: int | None = 4_000, diff --git a/src/scribae/translate_cli.py b/src/scribae/translate_cli.py index 499fb87..ac319cc 100644 --- a/src/scribae/translate_cli.py +++ b/src/scribae/translate_cli.py @@ -8,6 +8,7 @@ import typer import yaml +from scribae.llm import DEFAULT_MODEL_NAME from scribae.project import load_project from scribae.translate import ( LLMPostEditor, @@ -109,7 +110,7 @@ def translate( help="Additional regex patterns to protect.", ), postedit_model: str = typer.Option( # noqa: B008 - "mistral-nemo", + DEFAULT_MODEL_NAME, "--postedit-model", help="Model name for post-edit LLM pass.", ), diff --git a/src/scribae/write_cli.py b/src/scribae/write_cli.py index 5272694..792d4c3 100644 --- a/src/scribae/write_cli.py +++ b/src/scribae/write_cli.py @@ -4,9 +4,9 @@ import typer +from .llm import DEFAULT_MODEL_NAME from .project import default_project, load_project from .writer import ( - DEFAULT_WRITE_MODEL, EvidenceMode, WritingError, WritingValidationError, @@ -37,7 +37,7 @@ def write_command( help="Project name used to load projects/.yaml for prompt context.", ), model: str = typer.Option( # noqa: B008 - DEFAULT_WRITE_MODEL, + DEFAULT_MODEL_NAME, "--model", "-m", help="OpenAI/Ollama model name to request.", diff --git a/src/scribae/writer.py b/src/scribae/writer.py index d91b0a5..f43406c 100644 --- a/src/scribae/writer.py +++ b/src/scribae/writer.py @@ -14,14 +14,11 @@ from .brief import SeoBrief from .io_utils import NoteDetails, load_note -from .llm import make_model +from .llm import LLM_TIMEOUT_SECONDS, make_model from .project import ProjectConfig from .prompts_writer import SYSTEM_PROMPT, build_user_prompt from .snippets import SnippetSelection, build_snippet_block -DEFAULT_WRITE_MODEL = "mistral-nemo" -WRITER_TIMEOUT_SECONDS = 300.0 - Reporter = Callable[[str], None] | None @@ -253,9 +250,9 @@ async def _call() -> str: return str(output).strip() try: - return asyncio.run(asyncio.wait_for(_call(), WRITER_TIMEOUT_SECONDS)) + return asyncio.run(asyncio.wait_for(_call(), LLM_TIMEOUT_SECONDS)) except TimeoutError as exc: - raise WritingLLMError(f"LLM request timed out after {int(WRITER_TIMEOUT_SECONDS)} seconds.") from exc + raise WritingLLMError(f"LLM request timed out after {int(LLM_TIMEOUT_SECONDS)} seconds.") from exc except KeyboardInterrupt: raise except Exception as exc: # pragma: no cover - surfaced to CLI @@ -312,7 +309,6 @@ def _report(reporter: Reporter, message: str) -> None: __all__ = [ - "DEFAULT_WRITE_MODEL", "EvidenceMode", "WritingContext", "WritingError",