Skip to content

Commit

Permalink
chore: lazy importing
Browse files Browse the repository at this point in the history
  • Loading branch information
phil65 committed Feb 1, 2025
1 parent 6f41c99 commit a277031
Show file tree
Hide file tree
Showing 13 changed files with 54 additions and 35 deletions.
3 changes: 2 additions & 1 deletion src/llmling/config/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
BaseModel,
ConfigDict,
)
import upath


class ConfigModel(BaseModel):
Expand Down Expand Up @@ -62,6 +61,8 @@ def save(self, path: str | os.PathLike[str], overwrite: bool = False) -> None:
OSError: If file cannot be written
ValueError: If path is invalid
"""
import upath

try:
yaml_str = self.model_dump_yaml()
file_path = upath.UPath(path)
Expand Down
5 changes: 2 additions & 3 deletions src/llmling/config/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
import re
from typing import TYPE_CHECKING, Self

from upath import UPath

from llmling.config.models import Config
from llmling.core import exceptions
from llmling.core.log import get_logger, setup_logging
Expand Down Expand Up @@ -172,7 +170,8 @@ def validate(self) -> list[str]:

def _validate_requirements(self, config: Config) -> list[str]:
"""Validate requirement specifications."""
# Validate requirement format
from upath import UPath

warnings = [
f"Invalid requirement format: {req}"
for req in config.global_settings.requirements
Expand Down
8 changes: 6 additions & 2 deletions src/llmling/config/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
field_validator,
model_validator,
)
import upath
import yamling

from llmling import config_resources
from llmling.config.base import ConfigModel
Expand Down Expand Up @@ -269,6 +267,8 @@ class PathResource(BaseResource):

def validate_resource(self) -> list[str]:
"""Check if path exists for local files."""
import upath

warnings = []
path = upath.UPath(self.path)
prefixes = ("http://", "https://")
Expand All @@ -281,6 +281,8 @@ def validate_resource(self) -> list[str]:
@property
def supports_watching(self) -> bool:
"""Whether this resource instance supports watching."""
import upath

path = upath.UPath(self.path)
if not path.exists():
msg = f"Cannot watch non-existent path: {self.path}"
Expand Down Expand Up @@ -639,6 +641,8 @@ def from_file(cls, path: str | os.PathLike[str]) -> Self:
Raises:
ConfigError: If loading fails
"""
import yamling

logger.debug("Loading configuration from %s", path)

try:
Expand Down
3 changes: 2 additions & 1 deletion src/llmling/config/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from typing import TYPE_CHECKING, Any, Literal, Self

import logfire
from upath import UPath

from llmling.config.manager import ConfigManager
from llmling.config.models import BaseResource, PathResource
Expand Down Expand Up @@ -930,6 +929,8 @@ def register_file_prompt(
... format="markdown"
... )
"""
from upath import UPath

try:
if not UPath(path).exists():
msg = f"Prompt file not found: {path}"
Expand Down
8 changes: 5 additions & 3 deletions src/llmling/config/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@
import json
from typing import TYPE_CHECKING, TypedDict

import platformdirs
from upath import UPath

from llmling.core.log import get_logger


Expand Down Expand Up @@ -43,6 +40,9 @@ class ConfigStore:

def __init__(self, filename: str | None = None) -> None:
"""Initialize store with default paths."""
import platformdirs
from upath import UPath

llmling_dir = platformdirs.user_config_dir("llmling")
self.config_dir = UPath(llmling_dir)
name = filename or "configs.json"
Expand Down Expand Up @@ -91,6 +91,8 @@ def add_config(self, name: str, path: str | os.PathLike[str]) -> None:
IsADirectoryError: If path points to a directory
"""
# Basic validation
from upath import UPath

if not name.isidentifier():
msg = f"Invalid config name: {name} (must be a valid Python identifier)"
raise ValueError(msg)
Expand Down
4 changes: 2 additions & 2 deletions src/llmling/core/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
from datetime import datetime
import logging
from logging.handlers import RotatingFileHandler
from pathlib import Path
import sys
from typing import TYPE_CHECKING

import logfire
import platformdirs
from upath import UPath


if TYPE_CHECKING:
Expand All @@ -29,7 +29,7 @@
}

# Get platform-specific log directory
LOG_DIR = UPath(platformdirs.user_log_dir("llmling", "llmling"))
LOG_DIR = Path(platformdirs.user_log_dir("llmling", "llmling"))
TIMESTAMP = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
LOG_FILE = LOG_DIR / f"llmling_{TIMESTAMP}.log"

Expand Down
8 changes: 6 additions & 2 deletions src/llmling/prompts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
import os # noqa: TC003
from typing import TYPE_CHECKING, Annotated, Any, Literal, get_type_hints

from docstring_parser import parse as parse_docstring
from pydantic import BaseModel, ConfigDict, Field, ImportString
import upath

from llmling.completions import CompletionFunction # noqa: TC001
from llmling.core.log import get_logger
Expand Down Expand Up @@ -271,6 +269,8 @@ def from_callable(
Raises:
ValueError: If callable cannot be imported or is invalid
"""
from docstring_parser import parse as parse_docstring

completions = completions or {}
# Import if string path provided
if isinstance(fn, str):
Expand Down Expand Up @@ -350,6 +350,8 @@ class FilePrompt(BasePrompt):
@property
def messages(self) -> list[PromptMessage]:
"""Get messages from file content."""
import upath

content = upath.UPath(self.path).read_text("utf-8")

match self.fmt:
Expand All @@ -371,6 +373,8 @@ async def format(
self, arguments: dict[str, Any] | None = None
) -> list[PromptMessage]:
"""Format the file content with arguments."""
import upath

args = arguments or {}
self.validate_arguments(args)

Expand Down
11 changes: 7 additions & 4 deletions src/llmling/resources/loaders/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@

from typing import TYPE_CHECKING, Any, ClassVar

import upath
from upath import UPath

from llmling.config.models import PathResource
from llmling.core import exceptions
from llmling.core.log import get_logger
Expand Down Expand Up @@ -46,12 +43,14 @@ def get_name_from_uri(cls, uri: str) -> str:

def create_uri(self, *, name: str, params: dict[str, str] | None = None) -> str:
"""Create a URI based on resource path basename or explicit URI."""
from upath import UPath

try:
if self.context and self.context.resource:
if self.context.resource.uri:
return paths.path_to_uri(self.context.resource.uri)
# Use basename of the configured path
path = upath.UPath(self.context.resource.path)
path = UPath(self.context.resource.path)
return paths.path_to_uri(path.name)
# Fallback to name if no context
return paths.path_to_uri(name)
Expand All @@ -66,6 +65,8 @@ async def get_completions(
**options: Any,
) -> list[str]:
"""Get path completions."""
from upath import UPath

try:
# Handle both absolute and relative paths
path = UPath(current_value) if current_value else UPath()
Expand All @@ -88,6 +89,8 @@ async def _load_impl(
processor_registry: ProcessorRegistry | None,
) -> AsyncIterator[LoadedResource]:
"""Load content from file(s)."""
from upath import UPath

try:
path = UPath(resource.path)

Expand Down
4 changes: 2 additions & 2 deletions src/llmling/resources/loaders/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
import os
from typing import TYPE_CHECKING, Any

import upath

from llmling.config.models import PathResource, TextResource
from llmling.core import exceptions
from llmling.core.baseregistry import BaseRegistry
Expand Down Expand Up @@ -91,6 +89,8 @@ def find_loader_for_uri(self, uri: str) -> ResourceLoader[Any]:

def _validate_item(self, item: Any) -> ResourceLoader[Any]:
"""Validate and possibly transform item before registration."""
import upath

match item:
case str() if "\n" in item:
resource = TextResource(content=item)
Expand Down
17 changes: 10 additions & 7 deletions src/llmling/resources/loaders/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
from time import time
from typing import TYPE_CHECKING, ClassVar

import upath

from llmling.config.models import RepositoryResource
from llmling.core import exceptions
from llmling.core.log import get_logger
Expand All @@ -17,6 +15,7 @@

if TYPE_CHECKING:
from collections.abc import AsyncIterator
import os

from git.repo import Repo

Expand Down Expand Up @@ -56,20 +55,23 @@ def _cache_repo(self, repo_url: str, repo: Repo) -> None:

def _create_resource(
self,
path: upath.UPath,
path: str | os.PathLike[str],
name: str,
resource: RepositoryResource,
) -> LoadedResource:
"""Create LoadedResource from file."""
import upath

path_obj = upath.UPath(path)
try:
content = path.read_text("utf-8")
content = path_obj.read_text("utf-8")
description = f"Repository content from {resource.repo_url} ({resource.ref})"
return create_loaded_resource(
content=content,
source_type="repository",
uri=self.create_uri(name=name),
mime_type=guess_mime_type(path),
name=resource.description or str(path.name),
name=resource.description or path_obj.name,
description=description,
additional_metadata={
"repo": resource.repo_url,
Expand All @@ -88,9 +90,10 @@ async def _load_impl(
processor_registry: ProcessorRegistry | None,
) -> AsyncIterator[LoadedResource]:
"""Load git content."""
try:
import git
import git
import upath

try:
repo = self._get_cached_repo(resource.repo_url)
if not repo:
with tempfile.TemporaryDirectory() as tmp_dir:
Expand Down
4 changes: 2 additions & 2 deletions src/llmling/resources/watching/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

from typing import TYPE_CHECKING

from upath import UPath

from llmling.core.log import get_logger


Expand All @@ -30,6 +28,8 @@ def load_patterns(
Returns:
Combined list of patterns
"""
from upath import UPath

result: list[str] = []

# Add configured patterns
Expand Down
4 changes: 2 additions & 2 deletions src/llmling/resources/watching/watcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
from pathlib import Path
from typing import TYPE_CHECKING

import upath

from llmling.core.log import get_logger
from llmling.resources.watching.utils import load_patterns
from llmling.utils.watcher import FileWatcher
Expand Down Expand Up @@ -54,6 +52,8 @@ async def stop(self) -> None:

def add_watch(self, name: str, resource: BaseResource) -> None:
"""Add a watch for a resource."""
import upath

if not self._loop:
msg = "Watcher not started"
raise RuntimeError(msg)
Expand Down
10 changes: 6 additions & 4 deletions src/llmling/tools/openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@
from typing import TYPE_CHECKING, Any, Literal, TypeVar, Union
from uuid import UUID

import httpx
import upath
import yaml

from llmling.core.log import get_logger
from llmling.tools.toolsets import ToolSet

Expand Down Expand Up @@ -89,6 +85,8 @@ def __init__(
base_url: str = "",
headers: dict[str, str] | None = None,
) -> None:
import httpx

self.spec_url = spec
self.base_url = base_url
self.headers = headers or {}
Expand All @@ -115,6 +113,10 @@ def _ensure_loaded(self) -> None:

def _load_spec(self) -> Schema:
"""Load OpenAPI specification."""
import httpx
import upath
import yaml

try:
if self.spec_url.startswith(("http://", "https://")):
headers = {"Accept": "application/json"}
Expand Down

0 comments on commit a277031

Please sign in to comment.