Создана первоначальная структура обращения к языковым моделям

This commit is contained in:
Даниил Грабарь
2026-09-28 16:11:34 +03:00
commit 40cb7b6716
12 changed files with 2220 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
API_KEY=
BASE_URL=
PYDANTIC_AI_NO_BANNER=1
+16
View File
@@ -0,0 +1,16 @@
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info
# My files
.config/
# Virtual environments
.venv
# Credentianals
.env
+1
View File
@@ -0,0 +1 @@
3.13
View File
+31
View File
@@ -0,0 +1,31 @@
import asyncio
from dotenv import load_dotenv
# from pydantic import BaseModel
from pydantic_ai import Agent
from llm.config import load_config
from llm.factory import LLMModelProvider
load_dotenv()
async def main():
config = load_config("config.yml")
model = await LLMModelProvider.create_from_config(config.llm)
# model = await LLMModelProvider.create_codex(model_name='gpt-5.6-luna')
# model = LLMModelProvider.create_openai_compatible(model_name="qwen/qwen3-8b",
# api_key=os.getenv("API_KEY"),
# base_url=os.getenv("BASE_URL"))
agent = Agent(model=model)
result = await agent.run('Привет, дай мне ответ')
print(result.output)
if __name__ == "__main__":
asyncio.run(main())
+4
View File
@@ -0,0 +1,4 @@
llm:
provider: openai # openai, codex
# model: gpt-5.6-luna
model: qwen/qwen3-8b
View File
+39
View File
@@ -0,0 +1,39 @@
from pathlib import Path
from typing import Annotated, Literal
import yaml
from pydantic import BaseModel, Field
class CodexConfig(BaseModel):
provider: Literal["codex"]
model: str = "gpt-5.6-luna"
credential_path: Path | None = None
class OpenAiConfig(BaseModel):
provider: Literal["openai"]
model: str
api_key_env: str = "API_KEY"
base_url_env: str = "BASE_URL"
LLMConfig = Annotated[
CodexConfig | OpenAiConfig,
Field(discriminator="provider")
]
class AppConfig(BaseModel):
llm: LLMConfig
def load_config(path: str | Path = "config.yaml") -> AppConfig:
path = Path(path)
with path.open("r", encoding="utf-8") as file:
data = yaml.safe_load(file)
return AppConfig.model_validate(data)
+44
View File
@@ -0,0 +1,44 @@
import json
from dataclasses import asdict
from pathlib import Path
from pydantic_ai.providers.openai_codex import (
OpenAICodexCredentials,
OpenAICodexCredentialSource,
)
class FileCodexCredentionalSource(OpenAICodexCredentialSource):
def __init__(self,
path: Path,
encoding: str="utf-8",
ensure_ascii: bool=False,
indent: int=2):
super().__init__()
self._path = path
self._encoding=encoding
self._ensure_ascii=ensure_ascii
self._indent=indent
@property
def path(self):
return self._path
async def load(self) -> OpenAICodexCredentials:
return OpenAICodexCredentials(
**json.loads(
self._path.read_text(encoding=self._encoding)
)
)
async def save(self, credentials: OpenAICodexCredentials) -> None:
self._path.write_text(
json.dumps(
asdict(credentials),
ensure_ascii=self._ensure_ascii,
indent=self._indent
),
encoding=self._encoding
)
+84
View File
@@ -0,0 +1,84 @@
import os
import webbrowser
from pathlib import Path
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.models.openai_codex import OpenAICodexModel
from pydantic_ai.providers.openai import OpenAIProvider
from pydantic_ai.providers.openai_codex import (
OpenAICodexOAuthFlow,
OpenAICodexProvider,
)
from .config import CodexConfig, LLMConfig, OpenAiConfig
from .credentials import FileCodexCredentionalSource
class LLMModelProvider:
@staticmethod
def create_openai_compatible(model_name: str,
api_key: str,
base_url: str
) -> OpenAICodexModel:
provider = OpenAIProvider(base_url=base_url,
api_key=api_key)
return OpenAIChatModel(model_name=model_name,
provider=provider)
@staticmethod
async def create_codex(model_name: str='gpt-5.6-luna',
credentional_path: str | Path | None=None
) -> OpenAICodexModel:
if credentional_path is None:
credentional_path = Path.cwd() / ".config" / "codex-credentials.json"
source = FileCodexCredentionalSource(credentional_path)
if not source.path.exists():
flow = OpenAICodexOAuthFlow()
webbrowser.open(flow.authorization_url())
credentials = await flow.exchange_code_from_callback()
await source.save(credentials)
provider = OpenAICodexProvider(credential_source=source)
return OpenAICodexModel(model_name=model_name,
provider=provider)
@staticmethod
async def create_from_config(config: LLMConfig):
if isinstance(config, CodexConfig):
return await LLMModelProvider.create_codex(
model_name=config.model,
credentional_path=config.credential_path
)
if isinstance(config, OpenAiConfig):
api_key = os.getenv(config.api_key_env)
base_url=os.getenv(config.base_url_env)
if not api_key:
raise ValueError(
f"Environment variable "
f"{config.api_key_env!r} is not set"
)
if not base_url:
raise ValueError(
f"Environment variable "
f"{config.base_url_env!r} is not set"
)
return LLMModelProvider.create_openai_compatible(
model_name=config.model,
api_key=api_key,
base_url=base_url
)
raise ValueError(f"Unsupported LLM config: {type(config)}")
+11
View File
@@ -0,0 +1,11 @@
[project]
name = "llm-robot-control"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"pydantic>=2.13.5",
"pydantic-ai>=2.51.0",
"python-dotenv>=1.2.3",
]
Generated
+1987
View File
File diff suppressed because it is too large Load Diff