diff --git a/services/api-server/src/api_server/ai_structure_service.py b/services/api-server/src/api_server/ai_structure_service.py index f50d166..ee400f1 100644 --- a/services/api-server/src/api_server/ai_structure_service.py +++ b/services/api-server/src/api_server/ai_structure_service.py @@ -27,7 +27,7 @@ def prepare_ai_structure( display_output_path: str | None = None, ) -> dict[str, Any]: if not input_path.exists(): - raise FileNotFoundError(f"Input path not found: {input_path}") + raise FileNotFoundError(f"Входная папка не найдена: {input_path}") output_path.mkdir(parents=True, exist_ok=True) files = _inventory(input_path) parseable = any(Path(item["relative_path"]).suffix.casefold() in _PARSEABLE_SUFFIXES for item in files) @@ -40,14 +40,14 @@ def prepare_ai_structure( try: normalized = normalize_one_c_project(input_path, project_id=project_id) except Exception as error: - diagnostics.append(f"NormalizedProject build failed: {error}") + diagnostics.append(f"Не удалось построить NormalizedProject: {error}") elif binaries: diagnostics.append( - "Input contains only binary .cf/.cfe files. Server-side AI structure requires Designer DumpConfigToFiles " - "or Windows Agent export before semantic indexing." + "Во входной папке есть только бинарные файлы .cf/.cfe. Для серверной подготовки структуры нужен " + "экспорт Designer DumpConfigToFiles или выгрузка через Windows Agent перед семантической индексацией." ) else: - diagnostics.append("No 1C metadata/XML/BSL files or .cf/.cfe binaries were found.") + diagnostics.append("Во входной папке не найдены файлы метаданных 1С, XML, BSL или бинарные .cf/.cfe.") codex_root = output_path / _codex_folder_name(project_id) manifest = _manifest( @@ -255,31 +255,31 @@ def _source_lookup(source_map: list[dict[str, Any]]) -> dict[str, str]: def _codex_agents_markdown(manifest: dict[str, Any]) -> str: - return f"""# AGENTS.md for 1C context package + return f"""# AGENTS.md для пакета контекста 1С -This folder is generated by SFERA for Codex. +Эта папка сгенерирована SFERA для Codex. -## How to use this folder +## Как использовать эту папку -- Treat this package as read-only context for project `{manifest['project_id']}`. -- Start with `README.md` and `context/project-overview.md`. -- Use `indexes/objects.json`, `indexes/modules.json`, and `indexes/edges.json` for precise navigation. -- Use `source/` for local copied BSL/XML/MDO text. Do not rely on the absolute source path from the machine that generated the package. -- Use `indexes/source-map.json` to map original source paths to local `source/...` paths. -- Use `raw/normalized_project.json` as the authoritative 1C metadata model when present. -- 1C modules, forms, commands, реквизиты, табличные части and rights are parts of owner 1C objects. Do not treat a form module as an independent detached source file. -- When writing BSL, preserve the owner object context from `qualified_name`, `lineage_id`, and `source`. -- If `status` is `export_required`, first export `.cf/.cfe` through 1C Designer/Windows Agent and regenerate this package from the exported files. +- Используйте пакет как контекст только для чтения для проекта `{manifest['project_id']}`. +- Начинайте с `README.md` и `context/project-overview.md`. +- Для точной навигации используйте `indexes/objects.json`, `indexes/modules.json` и `indexes/edges.json`. +- Для текста BSL/XML/MDO используйте локальную папку `source/`. Не опирайтесь на абсолютный путь исходников на машине, где пакет был сгенерирован. +- Используйте `indexes/source-map.json`, чтобы сопоставлять исходные пути с локальными путями `source/...`. +- Если есть `raw/normalized_project.json`, считайте его основной моделью метаданных 1С. +- Модули, формы, команды, реквизиты, табличные части и права являются частями объектов 1С-владельцев. Не рассматривайте модуль формы как отдельный независимый файл. +- При генерации BSL сохраняйте контекст объекта-владельца из `qualified_name`, `lineage_id` и `source`. +- Если `status` равен `export_required`, сначала выгрузите `.cf/.cfe` через 1C Designer/Windows Agent и затем пересоздайте пакет по выгруженным файлам. -## Important files +## Важные файлы -- `context/project-overview.md` - compact human context. -- `context/metadata-tree.md` - metadata tree extracted from NormalizedProject. -- `indexes/*.json` - machine-readable indexes for Codex search and reasoning. -- `source/` - local UTF-8 copies of BSL/XML/MDO source files. -- `objects/*.md` - object-level summaries. -- `modules/*.md` - module-level summaries. -- `raw/*.json` - full raw SFERA model. +- `context/project-overview.md` - краткий контекст для человека. +- `context/metadata-tree.md` - дерево метаданных из NormalizedProject. +- `indexes/*.json` - машиночитаемые индексы для поиска и рассуждений Codex. +- `source/` - локальные UTF-8 копии файлов BSL/XML/MDO. +- `objects/*.md` - карточки объектов. +- `modules/*.md` - карточки модулей. +- `raw/*.json` - полная сырая модель SFERA. """ @@ -287,31 +287,31 @@ def _codex_readme_markdown(manifest: dict[str, Any]) -> str: snapshot = manifest.get("snapshot") or {} normalized = manifest.get("normalized") or {} lines = [ - f"# Codex 1C Context: {manifest['project_id']}", + f"# Контекст 1С для Codex: {manifest['project_id']}", "", - f"- Status: `{manifest['status']}`", - f"- Source: `{manifest['input_path']}`", - f"- Files scanned: {manifest['files_count']}", - f"- SIR nodes: {snapshot.get('nodes', 0)}", - f"- SIR edges: {snapshot.get('edges', 0)}", - f"- Normalized objects: {normalized.get('objects', 0)}", + f"- Статус: `{manifest['status']}`", + f"- Источник: `{manifest['input_path']}`", + f"- Просканировано файлов: {manifest['files_count']}", + f"- Узлов SIR: {snapshot.get('nodes', 0)}", + f"- Связей SIR: {snapshot.get('edges', 0)}", + f"- Нормализованных объектов: {normalized.get('objects', 0)}", "", - "Copy this whole folder into the Codex project when you want Codex to write code for this 1C configuration.", - "The package includes a local `source/` folder, so Codex can inspect BSL/XML/MDO files after the folder is moved.", + "Перенесите эту папку целиком в проект Codex, когда хотите, чтобы Codex писал код для этой конфигурации 1С.", + "Пакет включает локальную папку `source/`, поэтому Codex сможет читать BSL/XML/MDO после переноса папки.", ] if manifest.get("diagnostics"): - lines.extend(["", "## Diagnostics"]) + lines.extend(["", "## Диагностика"]) lines.extend(f"- {item}" for item in manifest["diagnostics"]) return "\n".join(lines) + "\n" def _codex_start_here_markdown(manifest: dict[str, Any]) -> str: - return f"""# Start Here For Codex + return f"""# Начните здесь для Codex -Project: `{manifest['project_id']}` -Status: `{manifest['status']}` +Проект: `{manifest['project_id']}` +Статус: `{manifest['status']}` -Read in this order: +Читайте в таком порядке: 1. `AGENTS.md` 2. `README.md` @@ -322,13 +322,13 @@ Read in this order: 7. `indexes/edges.json` 8. `source/` -When generating code: +При генерации кода: -- Locate the owner 1C object first. -- Then inspect its module/form/command context. -- Prefer local copied files under `source/` for exact source text. -- Use `raw/normalized_project.json` when object structure matters more than raw XML layout. -- Use `indexes/source-map.json` if you need to map SFERA source references to local package paths. +- Сначала найдите объект 1С-владельца. +- Затем изучите контекст его модуля, формы и команды. +- Для точного текста исходника предпочитайте локальные копии в `source/`. +- Используйте `raw/normalized_project.json`, когда структура объекта важнее, чем сырой XML. +- Используйте `indexes/source-map.json`, если нужно сопоставить ссылки SFERA с локальными путями пакета. """ @@ -375,14 +375,14 @@ def _object_markdown(item: dict[str, Any]) -> str: [ f"# {item.get('qualified_name') or item.get('name')}", "", - f"- Kind: `{item.get('kind')}`", - f"- Name: `{item.get('name')}`", + f"- Вид: `{item.get('kind')}`", + f"- Имя: `{item.get('name')}`", f"- Lineage: `{item.get('lineage_id')}`", f"- Semantic: `{item.get('semantic_id')}`", - f"- Source: `{item.get('source')}`", - f"- Local source: `{local_source}`", + f"- Источник: `{item.get('source')}`", + f"- Локальный исходник: `{local_source}`", "", - "## Attributes", + "## Атрибуты", "```json", json.dumps(item.get("attributes") or {}, ensure_ascii=False, indent=2, default=str), "```", @@ -396,12 +396,12 @@ def _module_markdown(item: dict[str, Any]) -> str: [ f"# {item.get('qualified_name') or item.get('name')}", "", - f"- Name: `{item.get('name')}`", + f"- Имя: `{item.get('name')}`", f"- Lineage: `{item.get('lineage_id')}`", - f"- Source: `{item.get('source')}`", - f"- Local source: `{local_source}`", + f"- Источник: `{item.get('source')}`", + f"- Локальный исходник: `{local_source}`", "", - "## Module Attributes", + "## Атрибуты модуля", "```json", json.dumps(item.get("attributes") or {}, ensure_ascii=False, indent=2, default=str), "```", @@ -431,7 +431,7 @@ def _source_ref_local_path(source_ref: object | None, source_lookup: dict[str, s def _normalized_tree_markdown(normalized: NormalizedProject) -> str: - lines = [f"# Metadata Tree: {normalized.project_id or 'project'}", ""] + lines = [f"# Дерево метаданных: {normalized.project_id or 'project'}", ""] for group in normalized.configuration.groups: lines.append(f"## {group.name}") if not group.objects: @@ -481,33 +481,33 @@ def _ai_modules(snapshot: SirSnapshot, source_lookup: dict[str, str] | None = No def _ai_context_markdown(manifest: dict[str, Any], snapshot: SirSnapshot | None, normalized: NormalizedProject | None) -> str: lines = [ - f"# SFERA AI structure: {manifest['project_id']}", + f"# Структура SFERA для ИИ: {manifest['project_id']}", "", - f"- Status: {manifest['status']}", - f"- Source files: {manifest['files_count']}", - f"- Artifacts: {', '.join(manifest['artifacts'])}", + f"- Статус: {manifest['status']}", + f"- Исходных файлов: {manifest['files_count']}", + f"- Артефакты: {', '.join(manifest['artifacts'])}", ] if snapshot is not None: lines.extend( [ - f"- SIR nodes: {len(snapshot.nodes)}", - f"- SIR edges: {len(snapshot.edges)}", - f"- Snapshot hash: {snapshot.snapshot_hash}", + f"- Узлов SIR: {len(snapshot.nodes)}", + f"- Связей SIR: {len(snapshot.edges)}", + f"- Хеш снимка: {snapshot.snapshot_hash}", ] ) if normalized is not None: - lines.append(f"- Normalized metadata groups: {len(normalized.configuration.groups)}") + lines.append(f"- Групп нормализованных метаданных: {len(normalized.configuration.groups)}") if manifest["diagnostics"]: lines.append("") - lines.append("## Diagnostics") + lines.append("## Диагностика") lines.extend(f"- {item}" for item in manifest["diagnostics"]) lines.extend( [ "", - "## How AI should use this package", - "- Use `normalized_project.json` as the authoritative 1C object model.", - "- Use `sir_snapshot.json`, `ai_objects.json`, `ai_modules.json`, and `ai_edges.json` for code navigation and impact analysis.", - "- Treat modules/forms/commands as parts of their owner 1C objects, not as detached text files.", + "## Как ИИ должен использовать этот пакет", + "- Используйте `normalized_project.json` как основную модель объектов 1С.", + "- Используйте `sir_snapshot.json`, `ai_objects.json`, `ai_modules.json` и `ai_edges.json` для навигации по коду и анализа влияния.", + "- Рассматривайте модули, формы и команды как части объектов 1С-владельцев, а не как отдельные текстовые файлы.", ] ) return "\n".join(lines) + "\n" @@ -515,15 +515,15 @@ def _ai_context_markdown(manifest: dict[str, Any], snapshot: SirSnapshot | None, def _export_plan_markdown(project_id: str, input_path: Path, output_path: Path, binaries: list[dict[str, Any]], parseable: bool) -> str: lines = [ - f"# 1C export plan for {project_id}", + f"# План выгрузки 1С для {project_id}", "", - f"- Input: `{input_path}`", - f"- Output: `{output_path}`", + f"- Вход: `{input_path}`", + f"- Выход: `{output_path}`", ] if parseable: - lines.append("- Metadata files were found; semantic processing was executed directly.") + lines.append("- Найдены файлы метаданных; семантическая обработка выполнена напрямую.") if binaries: - lines.extend(["", "## Binary .cf/.cfe files", ""]) + lines.extend(["", "## Бинарные файлы .cf/.cfe", ""]) for item in binaries: lines.append(f"- `{item['relative_path']}`") lines.extend( diff --git a/services/api-server/src/api_server/html5_ai_structure.py b/services/api-server/src/api_server/html5_ai_structure.py index 45de7fa..f03e32a 100644 --- a/services/api-server/src/api_server/html5_ai_structure.py +++ b/services/api-server/src/api_server/html5_ai_structure.py @@ -16,16 +16,16 @@ def render_html5_ai_structure_page( ) -> str: project_nav = "\n".join(_project_link(project, project_id) for project in projects) return _page( - f"SFERA AI Structure - {project_id}", + f"SFERA Структура для ИИ - {project_id}", f"""
{_topbar(project_id, project_nav)}
@@ -53,35 +53,35 @@ def render_html5_ai_structure_form(project_id: str, *, saved_credentials: dict[s hx-swap="innerHTML" hx-indicator="[data-ai-structure-progress]" > -