auto-sync: 2026-04-18 20:40:01

This commit is contained in:
Stream
2026-04-18 20:40:01 +03:00
parent e72440a31f
commit c7ee37f414
5 changed files with 11 additions and 351 deletions

View File

@@ -102,14 +102,6 @@
- Скилл открывается ДО начала действий, а не после того как застряла
- Исключение: диагностика когда непонятно куда смотреть — но потом сверяться со скиллом
- Факт нарушения: 12.04.2026 — HA audit делала вручную, скилл не открыла → потеряла время на костыли
- **Удалён** из HA (компонент + строка в configuration.yaml)
- Был создан Dev-агентом без явной хотелки Славы — оказался не нужен
- Запрос на удаление: Слава, 12.04.2026
## Учёт токенов Dev-агента
- Реальные данные — из OpenRouter dashboard
- Формула: `(input×$3 + output×$15) / 1M` (Sonnet 4.6)
- Лог: `tasks/token-log.md`, формат: `DEV-XXX | XK in / XK out → $X.XX`
## Проекты

View File

@@ -227,6 +227,17 @@ python3 scripts/ontology.py list --type Person
- `references/schema.md` — Full type definitions and constraint patterns
- `references/queries.md` — Query language and traversal examples
## Current OpenClaw Convention
Use ontology as the source of truth for project/work tracking:
- **Task without Project does not exist**
- `Project` should carry readable `folder` and `doc_path`
- `Task` should carry `project`, `folder`, and `doc_path`
- Human-facing names stay in `name` / `title`; internal ids stay internal
- `tasks/<project-slug>/` is the project root; task folders live under `tasks/<project-slug>/TASKS/<status>/<task-slug>/`
- If a work item does not fit an existing project, create or clarify the project first, then the task
## Instruction Scope
Runtime instructions operate on local files (`memory/ontology/graph.jsonl` and `memory/ontology/schema.yaml`) and provide CLI usage for create/query/relate/validate; this is within scope. The skill reads/writes workspace files and will create the `memory/ontology` directory when used. Validation includes property/enum/forbidden checks, relation type/cardinality validation, acyclicity for relations marked `acyclic: true`, and Event `end >= start` checks; other higher-level constraints may still be documentation-only unless implemented in code.

View File

@@ -1,136 +0,0 @@
# DEV-014: Аудит и настройка моделей в OpenClaw
**Дата:** 27 марта 2026
**Статус:** ✅ Завершено
---
## 1. Что нашёл в документации
### `/app/docs/providers/google.md`
- Провайдер: `google`
- Аутентификация: `GEMINI_API_KEY` или `GOOGLE_API_KEY` (из `~/.openclaw/.env`)
- Синтаксис модели в конфиге: `google/gemini-3.1-pro-preview` (prefix `google/` + ID модели)
- Если Gateway запущен как демон — `GEMINI_API_KEY` должен быть в `~/.openclaw/.env` ✅ (уже так)
- Альтернативный провайдер: `google-gemini-cli` (OAuth, unofficial)
### `/app/docs/providers/models.md`
- Формат задания модели: `provider/model` (например `google/gemini-2.5-pro`)
- Для OpenRouter: `openrouter/<provider>/<model>`
### `/app/docs/providers/openrouter.md`
- Модели OpenRouter: `openrouter/<provider>/<model>` (например `openrouter/anthropic/claude-sonnet-4.6`)
- Принципиальное отличие от Google: OpenRouter имеет промежуточный слой (один ключ → много провайдеров)
---
## 2. Что было неправильно в конфиге
### Проблема: Устаревшие/несуществующие ID моделей Google
Были добавлены следующие модели, которые **не существуют в Google API**:
| Неправильный ID | HTTP ответ | Причина ошибки |
|-----------------|------------|----------------|
| `gemini-2.5-pro-preview-03-25` | **404 NOT FOUND** | Устаревший preview ID (был заменён стабильным) |
| `gemini-2.5-flash-preview-04-17` | **404 NOT FOUND** | Устаревший preview ID (был заменён стабильным) |
Проверка через `GET /v1beta/models?key=...` показала, что этих моделей больше нет в API.
---
## 3. Что исправил
### Удалены невалидные модели
```
- google/gemini-2.5-pro-preview-03-25
- gemini-2.5-pro-preview-03-25
- google/gemini-2.5-flash-preview-04-17
- gemini-2.5-flash-preview-04-17
```
### Добавлены актуальные модели (проверено через Google ListModels API)
```json
"google/gemini-2.5-pro": {},
"gemini-2.5-pro": {},
"google/gemini-2.5-flash": {},
"gemini-2.5-flash": {},
"google/gemini-2.5-flash-lite": {},
"gemini-2.5-flash-lite": {},
"google/gemini-2.0-flash": {},
"gemini-2.0-flash": {},
"google/gemini-3.1-pro-preview": {},
"gemini-3.1-pro-preview": {},
"google/gemini-3.1-flash-lite-preview": {},
"gemini-3.1-flash-lite-preview": {}
```
Все модели добавлены **двумя записями**с prefix `google/` и без (согласно правилу из MEMORY.md).
### Почему профиль `google:default` корректен
```json
"google:default": {
"provider": "google",
"mode": "api_key"
}
```
Профиль настроен правильно. Ключ `GEMINI_API_KEY` читается из `~/.openclaw/.env` ✅.
---
## 4. Результаты тестирования
### Полный список актуальных моделей Google (через ListModels API)
Из API получен список всех моделей, поддерживающих `generateContent`:
| ID модели | Название |
|-----------|---------|
| `gemini-2.5-flash` | Gemini 2.5 Flash |
| `gemini-2.5-pro` | Gemini 2.5 Pro |
| `gemini-2.0-flash` | Gemini 2.0 Flash |
| `gemini-2.0-flash-001` | Gemini 2.0 Flash 001 |
| `gemini-2.0-flash-lite` | Gemini 2.0 Flash-Lite |
| `gemini-2.5-flash-lite` | Gemini 2.5 Flash-Lite |
| `gemini-3.1-pro-preview` | Gemini 3.1 Pro Preview |
| `gemini-3.1-flash-lite-preview` | Gemini 3.1 Flash Lite Preview |
| `gemini-3-pro-preview` | Gemini 3 Pro Preview |
| `gemini-3-flash-preview` | Gemini 3 Flash Preview |
### Тестовые вызовы
| Модель | Статус | Результат |
|--------|--------|-----------|
| `gemini-2.5-flash` | ✅ 200 OK | `Hi` (корректный ответ) |
| `gemini-2.5-pro` | ⚠️ 429 | Rate limit (Free Tier) — модель существует, лимит квоты |
| `gemini-3.1-pro-preview` | ⚠️ 429 | Rate limit (Free Tier) — модель существует, лимит квоты |
| `gemini-2.5-pro-preview-03-25` | ❌ 404 | Модель не существует |
| `gemini-2.5-flash-preview-04-17` | ❌ 404 | Модель не существует |
**Вывод:** API работает. Free Tier имеет строгие rate limits, но модели валидны.
---
## 5. Нужна ли перезагрузка гейтвея?
**Да, нужна** — изменения в `openclaw.json` (список `agents.defaults.models`) вступают в силу только после перезапуска Gateway.
```bash
# Перезапуск Gateway
openclaw gateway restart
```
Или вручную:
```bash
kill -9 $(pgrep -f "openclaw gateway") && openclaw gateway &
```
---
## Итог
- ✅ Документация изучена — Google provider работает через `google/` prefix + `GEMINI_API_KEY` в `.env`
- ✅ Обнаружены 2 невалидных ID моделей (устаревшие preview-версии с датой)
- ✅ Конфиг исправлен: добавлены актуальные стабильные модели Google (2.5-pro, 2.5-flash, 2.0-flash, 2.5-flash-lite, 3.1-pro-preview, 3.1-flash-lite-preview)
-Все модели добавлены двумя записями (с prefix `google/` и без)
- ✅ API протестирован — `gemini-2.5-flash` отвечает корректно (HTTP 200)
- ⚠️ Free Tier активен — есть rate limits, но это ограничение плана, не конфигурации
- 🔄 Требуется перезапуск Gateway для применения изменений

View File

@@ -1,116 +0,0 @@
import json
import os
from datetime import datetime, timezone
import glob
def parse_iso(timestamp):
# Parse ISO timestamp with timezone
try:
dt = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
return dt
except Exception as e:
print(f'Error parsing {timestamp}: {e}')
return None
def process_session_file(path):
today = datetime(2026, 3, 22, tzinfo=timezone.utc)
total_input = 0
total_output = 0
total_cost = 0.0
model_counts = {}
with open(path, 'r') as f:
for line_num, line in enumerate(f, 1):
line = line.strip()
if not line:
continue
try:
data = json.loads(line)
except json.JSONDecodeError as e:
continue
# Check timestamp for today
timestamp = data.get('timestamp')
if not timestamp:
continue
dt = parse_iso(timestamp)
if not dt:
continue
if dt.date() != today.date():
continue
# We need messages with usage (assistant responses)
if data.get('type') == 'message':
msg = data.get('message', {})
if msg.get('role') == 'assistant' and 'usage' in msg:
usage = msg['usage']
input_tokens = usage.get('input', 0)
output_tokens = usage.get('output', 0)
cache_read = usage.get('cacheRead', 0)
cache_write = usage.get('cacheWrite', 0)
# cost may be inside usage['cost']
cost = usage.get('cost', {})
cost_total = cost.get('total', 0.0)
model = msg.get('model', 'unknown')
total_input += input_tokens
total_output += output_tokens
total_cost += cost_total
# Track per model
if model not in model_counts:
model_counts[model] = {'input': 0, 'output': 0, 'cost': 0.0}
model_counts[model]['input'] += input_tokens
model_counts[model]['output'] += output_tokens
model_counts[model]['cost'] += cost_total
return total_input, total_output, total_cost, model_counts
sessions_dir = '/home/node/.openclaw/agents/main/sessions'
jsonl_files = glob.glob(os.path.join(sessions_dir, '*.jsonl'))
print('📊 Сводка использования токенов за сегодня (2026-03-22)')
print('=' * 60)
overall_input = 0
overall_output = 0
overall_cost = 0.0
all_model_counts = {}
for file_path in jsonl_files:
file_name = os.path.basename(file_path)
print(f'\n📁 Файл сессии: {file_name}')
inp, out, cost, models = process_session_file(file_path)
print(f' Входные токены: {inp:,}')
print(f' Выходные токены: {out:,}')
print(f' Примерная стоимость: ${cost:.6f}')
if models:
for model, counts in models.items():
print(f' Модель: {model}')
print(f' вход: {counts[\"input\"]:,}, выход: {counts[\"output\"]:,}, стоимость: ${counts[\"cost\"]:.6f}')
# Aggregate across files
if model not in all_model_counts:
all_model_counts[model] = counts.copy()
else:
all_model_counts[model]['input'] += counts['input']
all_model_counts[model]['output'] += counts['output']
all_model_counts[model]['cost'] += counts['cost']
overall_input += inp
overall_output += out
overall_cost += cost
print('\n' + '=' * 60)
print('📈 ОБЩИЙ ИТОГ за сегодня:')
print(f' Всего входных токенов: {overall_input:,}')
print(f' Всего выходных токенов: {overall_output:,}')
print(f' Общая стоимость: ${overall_cost:.6f}')
print()
print('📋 По моделям:')
for model, counts in all_model_counts.items():
print(f'{model}')
print(f' вход: {counts[\"input\"]:,} токенов, выход: {counts[\"output\"]:,} токенов')
print(f' стоимость: ${counts[\"cost\"]:.6f}')
print()
print('💡 Примечание: стоимость может не включать кэшированные токены.')
print(' Данные основаны на записях сессий.')

View File

@@ -1,91 +0,0 @@
import json
import glob
from datetime import datetime
def parse_iso(timestamp):
try:
return datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
except:
return None
today = datetime(2026, 3, 22).date()
sessions_dir = '/home/node/.openclaw/agents/main/sessions'
jsonl_files = glob.glob(sessions_dir + '/*.jsonl')
claude_input = 0
claude_output = 0
claude_cost = 0.0
deepseek_input = 0
deepseek_output = 0
deepseek_cost = 0.0
for file_path in jsonl_files:
with open(file_path, 'r') as f:
for line in f:
line = line.strip()
if not line:
continue
try:
data = json.loads(line)
except:
continue
if data.get('type') != 'message':
continue
msg = data.get('message', {})
if msg.get('role') != 'assistant' or 'usage' not in msg:
continue
timestamp = data.get('timestamp')
if not timestamp:
continue
dt = parse_iso(timestamp)
if not dt or dt.date() != today:
continue
usage = msg['usage']
inp = usage.get('input', 0)
out = usage.get('output', 0)
cost = usage.get('cost', {}).get('total', 0.0)
model = msg.get('model', '')
if 'claude' in model.lower():
claude_input += inp
claude_output += out
claude_cost += cost
elif 'deepseek' in model.lower():
deepseek_input += inp
deepseek_output += out
deepseek_cost += cost
total_input = claude_input + deepseek_input
total_output = claude_output + deepseek_output
total_cost = claude_cost + deepseek_cost
print('=== Сводка использования OpenRouter за 22 марта 2026 ===')
print()
print('📊 По моделям:')
print('1. Claude Sonnet 4.6')
print(' • Входные токены:', f'{claude_input:,}')
print(' • Выходные токены:', f'{claude_output:,}')
print(' • Стоимость: $' + f'{claude_cost:.6f}')
print()
print('2. DeepSeek V3.2')
print(' • Входные токены:', f'{deepseek_input:,}')
print(' • Выходные токены:', f'{deepseek_output:,}')
print(' • Стоимость: $' + f'{deepseek_cost:.6f}')
print()
print('📈 ИТОГО:')
print(' Всего входных токенов:', f'{total_input:,}')
print(' Всего выходных токенов:', f'{total_output:,}')
print(' Общая стоимость: $' + f'{total_cost:.6f}')
print()
print('💎 Средняя стоимость за 1 тыс. входных токенов:')
if total_input > 0:
avg = total_cost / total_input * 1000
print(' $' + f'{avg:.6f}')
else:
print(' N/A')
print()
print('🔄 Переход на DeepSeek:')
print(' • Claude использовался до ~06:30 UTC')
print(' • DeepSeek используется с ~06:30 UTC')
print()
print('💡 Примечание: входные токены включают контекст всей сессии,')
print(' который пересылается при каждом запросе. Выходные токены — ответы модели.')