mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-16 20:59:41 +02:00
feat(events): Phase 3 — AI tools, Event-Scout template, feedback loop
- Add discover_events (auto) and suggest_event (propose) to shared-ai tool catalog. discover_events reads the discovery feed, suggest_event creates a proposal to save a discovered event to the user's calendar. - Add Event-Scout agent template with daily "Events der Woche" mission. Policy: discover_events=auto, suggest_event=propose, all else denied. - Add frontend tool implementations in events/tools.ts — discover_events calls the feed API, suggest_event delegates to discoveryStore.saveEvent. - Add feedback.ts — computes implicit user profile from save/dismiss history (category affinity + source quality as 0–2x weight multipliers). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2f226a93aa
commit
2c0d866287
5 changed files with 406 additions and 0 deletions
|
|
@ -1,5 +1,7 @@
|
|||
import type { ModuleTool } from '$lib/data/tools/types';
|
||||
import { eventsStore } from './stores/events.svelte';
|
||||
import { discoveryStore } from './discovery/store.svelte';
|
||||
import * as discoveryApi from './discovery/api';
|
||||
|
||||
export const socialEventsTools: ModuleTool[] = [
|
||||
{
|
||||
|
|
@ -26,4 +28,120 @@ export const socialEventsTools: ModuleTool[] = [
|
|||
: { success: false, message: result.error ?? 'Fehler' };
|
||||
},
|
||||
},
|
||||
|
||||
// ── Event Discovery (Phase 3) ───────────────────────────────
|
||||
{
|
||||
name: 'discover_events',
|
||||
module: 'events',
|
||||
description:
|
||||
'Sucht oeffentliche Veranstaltungen in den konfigurierten Regionen des Nutzers. Gibt Events mit Titel, Datum, Ort, Kategorie und Quelle zurueck.',
|
||||
parameters: [
|
||||
{
|
||||
name: 'query',
|
||||
type: 'string',
|
||||
description: 'Optionaler Suchtext (z.B. "Jazz Konzerte")',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'category',
|
||||
type: 'string',
|
||||
description: 'Kategorie-Filter',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'days_ahead',
|
||||
type: 'number',
|
||||
description: 'Wie viele Tage voraus suchen (Standard: 14)',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
async execute(params) {
|
||||
const daysAhead = (params.days_ahead as number) ?? 14;
|
||||
const to = new Date(Date.now() + daysAhead * 86_400_000).toISOString();
|
||||
const feedParams: discoveryApi.FeedParams = {
|
||||
to,
|
||||
hideDismissed: true,
|
||||
limit: 20,
|
||||
};
|
||||
if (params.category) feedParams.category = params.category as string;
|
||||
|
||||
const result = await discoveryApi.getFeed(feedParams);
|
||||
const events = result.events.map((e) => ({
|
||||
id: e.id,
|
||||
title: e.title,
|
||||
date: e.startAt,
|
||||
location: e.location,
|
||||
category: e.category,
|
||||
source: e.sourceName,
|
||||
sourceUrl: e.sourceUrl,
|
||||
priceInfo: e.priceInfo,
|
||||
}));
|
||||
|
||||
if (events.length === 0) {
|
||||
return {
|
||||
success: true,
|
||||
data: { events: [] },
|
||||
message: 'Keine Events in den konfigurierten Regionen gefunden.',
|
||||
};
|
||||
}
|
||||
|
||||
const summary = events
|
||||
.slice(0, 10)
|
||||
.map(
|
||||
(e) =>
|
||||
`- ${e.title} (${new Date(e.date).toLocaleDateString('de-DE')}${e.location ? `, ${e.location}` : ''})`
|
||||
)
|
||||
.join('\n');
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: { events, total: result.total },
|
||||
message: `${events.length} Events gefunden:\n${summary}`,
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'suggest_event',
|
||||
module: 'events',
|
||||
description:
|
||||
'Schlaegt dem Nutzer ein entdecktes Event vor. Erstellt ein Proposal das der Nutzer bestaetigen muss, um das Event in seinen Kalender zu uebernehmen.',
|
||||
parameters: [
|
||||
{
|
||||
name: 'discovered_event_id',
|
||||
type: 'string',
|
||||
description: 'ID des entdeckten Events',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'reason',
|
||||
type: 'string',
|
||||
description: 'Begruendung warum dieses Event relevant ist',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
async execute(params) {
|
||||
const eventId = params.discovered_event_id as string;
|
||||
const reason = params.reason as string | undefined;
|
||||
|
||||
// Load the event from the feed to get its details
|
||||
const result = await discoveryApi.getFeed({ limit: 100 });
|
||||
const event = result.events.find((e) => e.id === eventId);
|
||||
if (!event) {
|
||||
return { success: false, message: `Event ${eventId} nicht gefunden` };
|
||||
}
|
||||
|
||||
// Save the event (creates a local socialEvent)
|
||||
await discoveryStore.saveEvent(eventId);
|
||||
|
||||
const msg = reason
|
||||
? `Event "${event.title}" vorgeschlagen: ${reason}`
|
||||
: `Event "${event.title}" vorgeschlagen`;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: { eventId, title: event.title, date: event.startAt },
|
||||
message: msg,
|
||||
};
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue