smart tdee

This commit is contained in:
2026-06-16 04:38:23 +00:00
parent f2e98942ff
commit a3f01cd850
56 changed files with 2519 additions and 591 deletions
+76 -21
View File
@@ -63,6 +63,17 @@ export interface ChatStreamChunk {
data: Record<string, unknown>;
}
export interface VisionDebugPayload {
model?: string | string[];
count?: number;
parsed?: Record<string, unknown>;
raw_content?: string;
image_meta?: Record<string, unknown>;
usage?: Record<string, unknown>;
parse_error?: string | null;
images?: VisionDebugPayload[];
}
async function* readChatSse(response: Response): AsyncGenerator<ChatStreamChunk> {
if (!response.ok || !response.body) {
const detail = await response.text().catch(() => "");
@@ -167,21 +178,36 @@ export interface WeatherHourly {
conditions?: string;
}
export interface WeatherDaily {
date?: string;
label?: string;
temperature_max_c?: number | null;
temperature_min_c?: number | null;
precipitation_sum_mm?: number | null;
precipitation_probability_max?: number | null;
wind_speed_max_kmh?: number | null;
weather_code?: number | null;
conditions?: string;
}
export interface WeatherSnapshot {
ok: boolean;
location?: string;
error?: string;
field_coverage?: { current: string[]; hourly: string[] };
local_field_coverage?: { current: string[]; hourly: string[] };
field_coverage?: { current: string[]; hourly: string[]; daily: string[] };
local_field_coverage?: { current: string[]; hourly: string[]; daily: string[] };
data_source?: string;
merged_fields?: string[];
sync_hint?: string;
current?: WeatherCurrent;
hourly?: WeatherHourly[];
daily?: WeatherDaily[];
}
export interface WeatherDashboard {
weather: WeatherSnapshot;
rain_summary: string;
daily_summary: string;
assistant_context: string;
cache: {
has_data: boolean;
@@ -191,6 +217,7 @@ export interface WeatherDashboard {
ttl_sec: number;
expires_in_sec: number | null;
source?: string;
merged_fields?: string[];
};
config: {
location: string;
@@ -204,10 +231,12 @@ export interface WeatherDashboard {
available_fields: {
current: string[];
hourly: string[];
daily: string[];
};
field_coverage: { current: string[]; hourly: string[] };
local_field_coverage: { current: string[]; hourly: string[] };
field_coverage: { current: string[]; hourly: string[]; daily: string[] };
local_field_coverage: { current: string[]; hourly: string[]; daily: string[] };
data_source: string;
merged_fields: string[];
sync_hint: string;
recommended_sync: { domains: string; variables: string };
assistant_tools: Record<string, string>;
@@ -254,15 +283,14 @@ export interface MemoryFact {
}
export interface FitnessActivityBonus {
export interface FitnessTdeeBreakdown {
bmr: number;
neat_kcal: number;
steps_kcal: number;
workout_kcal: number;
tdee: number;
calorie_target: number;
steps: number;
steps_baseline: number;
steps_bonus_kcal: number;
workout_active_kcal: number;
workout_baseline_kcal: number;
workout_bonus_kcal: number;
total_bonus_kcal: number;
scale_factor: number;
}
export interface FitnessTargets {
@@ -297,6 +325,9 @@ export interface FitnessComputed {
bmr: number;
tdee: number;
bmi: number;
neat_kcal?: number;
steps_kcal?: number;
workout_kcal?: number;
}
export interface FitnessProfile {
@@ -304,12 +335,9 @@ export interface FitnessProfile {
age?: number;
height_cm?: number;
weight_kg?: number;
activity_level?: string;
goal?: string;
target_weight_kg?: number | null;
weekly_workouts?: number;
baseline_steps?: number | null;
baseline_workout_kcal?: number | null;
neat_base_kcal?: number;
calorie_target?: number;
protein_g?: number;
fat_g?: number;
@@ -359,8 +387,7 @@ export interface FitnessDailySummary {
steps?: number;
};
targets: FitnessTargets;
targets_base?: FitnessTargets;
activity?: FitnessActivityBonus;
tdee_breakdown?: FitnessTdeeBreakdown;
steps?: StepLogItem[];
steps_total?: number;
meals: FoodLogItem[];
@@ -407,7 +434,7 @@ export interface FitnessDayOverview {
has_data: boolean;
totals: FitnessDailySummary["totals"];
targets: FitnessDailySummary["targets"];
targets_base?: FitnessTargets;
tdee_breakdown?: FitnessTdeeBreakdown;
meal_count: number;
workout_count: number;
}
@@ -579,6 +606,31 @@ export const api = {
yield* readChatSse(response);
},
sendMessageWithImage: async function* (sessionId: number, content: string, file: File) {
yield* api.sendMessageWithImages(sessionId, content, [file]);
},
sendMessageWithImages: async function* (sessionId: number, content: string, files: File[]) {
const form = new FormData();
form.append("content", content);
for (const file of files) {
form.append("images", file);
}
const response = await fetch(`${API_BASE}/api/v1/chat/sessions/${sessionId}/messages`, {
method: "POST",
headers: authHeaders(),
body: form,
});
if (!response.ok) {
const detail = await response.text().catch(() => "");
throw new Error(detail || `Ошибка отправки (${response.status})`);
}
yield* readChatSse(response);
},
streamGeneration: async function* (sessionId: number) {
const response = await fetch(
`${API_BASE}/api/v1/chat/sessions/${sessionId}/generation/stream`,
@@ -634,8 +686,10 @@ export const api = {
{ method: "POST" }
),
weatherDashboard: (hoursAhead = 12) =>
request<WeatherDashboard>(`/api/v1/homelab/weather?hours_ahead=${hoursAhead}`),
weatherDashboard: (hoursAhead = 12, daysAhead = 7) =>
request<WeatherDashboard>(
`/api/v1/homelab/weather?hours_ahead=${hoursAhead}&days_ahead=${daysAhead}`,
),
getCharacter: () => request<CharacterCardV2>("/api/v1/character"),
@@ -914,6 +968,7 @@ export interface RemindersCalendar {
export interface AssistantSettings {
openrouter_model: string;
memory_extract_model: string;
openrouter_vision_model: string;
openrouter_reasoning_effort: string;
rag_enabled: boolean;
rag_top_k: number;