Skip to content

feat: expose campus and core study data as assistant tools #60

Description

@SebastianBoehler

Summary

Expose two groups of StudyOS_Agent capabilities as assistant tools:

  1. Public campus tools: Mensa menus and Tübingen campus/location search.
  2. Private study tools: combined ILIAS/Moodle tasks and deadlines, the ALMA study planner, and richer term-aware academic status.

This issue is the first concrete implementation consumer of the shared data/action capability contract in #54. Public tools may be used by local and cloud providers. Authenticated university data must be fetched on-device and remain local-only by default.

Motivation

Latest main has one shared StudyOsToolSpec catalog and executor for cloud and local providers, but several live app capabilities are UI-only and core study workflows from tue-api-wrapper are still missing:

  • CampusClient already loads Tübingen Mensa menus.
  • MapSearchClient already searches bounded Tübingen locations.
  • tue-api-wrapper has tested contracts for ILIAS tasks/assignment deadlines, Moodle calendar events, the ALMA study planner, and enrollment/exam status.

Adding these through one capability architecture gives the assistant useful, grounded data without routing student credentials through hosted infrastructure.

Dependencies and overlap

Slice 1: public campus tools

get_mensa_options

Inputs:

  • date (optional ISO date)
  • canteen (optional ID or name filter)
  • preference (optional: any, vegetarian, vegan)
  • limit (optional, bounded)

Output:

  • stable canteen/menu IDs where available
  • canteen name, date, line, meal items, dietary markers, and student price
  • source URL and freshness metadata
  • explicit empty/unavailable/upstream-error states

Behavior:

  • reuse CampusClient and shared model-level filtering/sorting
  • bound results before returning data to the model
  • use an explicit short TTL cache with manual refresh/invalidation
  • never return raw upstream JSON

search_campus_locations

Inputs:

  • query (required)
  • limit (optional, capped at 8)

Output:

  • stable result ID for follow-up actions
  • display name/address and coordinates
  • source and freshness metadata
  • explicit empty/unavailable/upstream-error states

Behavior:

  • reuse MapSearchClient and its Tübingen-bounded Nominatim request
  • do not transmit device GPS position
  • do not auto-open navigation; keep that as a separate future user-approved action using a returned result ID

Slice 2: private study tools

get_tasks

Inputs:

  • sources (optional subset of ilias, moodle; defaults to both)
  • limit (optional, capped at 50)

Output item contract:

  • stable source-qualified ID
  • source, courseTitle, title, itemType
  • startAt, dueAt, status, and actionable when known
  • local source URL/target for user navigation
  • result-level freshness and partial-failure metadata

Data sources:

  • ILIAS derived-task overview (IliasTaskItem: title, URL, type, start, end)
  • Moodle actionable dashboard/calendar events (MoodleDashboardEvent)

Behavior:

  • normalize dates to ISO-8601 with an explicit timezone when parsing is reliable
  • preserve raw date hints separately when an exact timestamp cannot be established
  • sort known due dates first, then undated tasks
  • return successful sources when one portal fails and include a bounded per-source failure instead of failing the whole tool

get_deadlines

Inputs:

  • days (optional, default 30, range 1–180)
  • sources (optional subset of ilias, moodle; defaults to both)
  • limit (optional, capped at 100)

Output item contract:

  • source-qualified ID and source
  • course, exercise/assignment title, requirement/status
  • dueAt plus the original due hint
  • source URL/target
  • result-level window, freshness, and partial-failure metadata

Data sources:

  • ILIAS course assignment traversal (IliasAssignmentDeadline / IliasExerciseAssignment)
  • Moodle calendar action events within the requested time window (MoodleCalendarPage)

Behavior:

  • filter only confirmed dated entries into the requested window
  • do not turn heuristic mail deadline matches into confirmed portal deadlines
  • deduplicate by source + canonical URL/ID + title + due time
  • cap course traversal and assignment scans before tool output
  • preserve portal attribution so the assistant never claims an ILIAS deadline came from Moodle or vice versa

get_study_planner

Inputs: none for the first version.

Output:

  • planner title and source URL
  • visible semesters (index, label, termLabel)
  • bounded modules with number, title, semester column/span, earned/required credits, progress, detail URL, and expandability
  • planner view-state flags and freshness metadata

Behavior:

  • port/reuse the wrapper's AlmaStudyPlannerPage contract and parser fixtures
  • return structured planner data, not raw HTML
  • keep module detail expansion out of the first version

Extend get_academic_status

Keep the existing tool name. Expand its response so it distinguishes:

  • selected/available term
  • course enrollment vs exam registration
  • status, attempt/category, schedule text, and detail URL when present
  • current report/parser notice and refresh timestamp

Do not infer “passed”, “registered”, or “cancelled” from missing/ambiguous fields. Preserve the portal's original status alongside any normalized status.

Local authenticated integration

The Flutter app must perform ALMA/ILIAS/Moodle requests locally using credentials from existing secure profile storage.

  • Never send the username/password to a hosted wrapper, cloud model, tool argument, prompt, result, trace, log, or URL.
  • Port the proven Shibboleth/SAML handoff, parser contracts, and fixtures from tue-api-wrapper into focused Dart clients, or consume them from a shared pure-Dart wrapper package if one is available.
  • Reuse a local authenticated session within one logical refresh where practical; inject/close HTTP clients deterministically.
  • Detect login redirects/session expiry explicitly and return authenticationRequired rather than empty data.
  • Keep portal clients/repositories separate from assistant serialization so UI, feed, and assistant can share the same capability.

Capability and tool architecture

  1. Add/reuse typed capability results containing:
    • data
    • state (fresh, stale, empty, unavailable, permissionDenied, authenticationRequired, failed)
    • source(s)
    • fetchedAt and optional expiresAt
    • reliability and bounded per-source failures
  2. Add policy metadata:
    • public tools: publicExternal, read-only, cloud/local allowed
    • study tools: privateLocal, read-only, local-only by default
  3. Register tools in the shared catalog so schemas, descriptions, traces, and provider filtering have one source of truth.
  4. Route execution through focused typed capability handlers rather than growing unrelated branches in StudyOsToolExecutor.
  5. Serialize compact bounded JSON only at the tool boundary.
  6. Inject clients/clocks so tests can use deterministic fakes; production must not silently fall back to mock data.

If #54 has not landed, implement only the smallest compatible contract subset and keep it easy for #54 to adopt without a second migration.

Cloud privacy enforcement

Cloud tool calls execute on-device, but their results are sent back to the configured cloud model. Labels alone are insufficient.

  • cloudToolDefinitions must omit privateLocal tools by default.
  • Local models may receive bounded private results on-device.
  • A future explicit user opt-in may allow selected redacted private results to a configured cloud provider; that consent flow is out of scope here.
  • Never place document bytes, attachments, cookies, auth headers, passwords, or full portal HTML in model context.

Caching and freshness

Use explicit per-capability caches, not hidden view futures:

  • Mensa: short TTL keyed by date/filter inputs
  • location search: short TTL keyed by normalized query
  • tasks/deadlines: short authenticated TTL with manual refresh and logout invalidation
  • study planner/academic status: longer TTL with manual refresh and logout invalidation

Never persist passwords, cookies, raw HTML, or attachment/document bytes in these caches. Consumers must be able to distinguish fresh, stale, empty, unavailable, and failed states.

Out of scope

  • official-document listing/download
  • automatic navigation/map opening
  • campus seats, buildings, events, sports, TIMMS, Praxisportal, or Anny
  • Moodle grades/messages/notifications beyond data needed for tasks/deadlines
  • ALMA/ILIAS/Moodle write actions
  • hosted credential storage/routing
  • silent mock production data

Acceptance criteria

Shared/public

  • Capability results include state, source, freshness, policy, and bounded failures.
  • get_mensa_options and search_campus_locations are advertised/executable in cloud and local assistant paths.
  • Inputs and output sizes are validated/capped.
  • Mensa filtering/sorting reuses app behavior; campus search remains bounded to Tübingen and does not transmit GPS.
  • HTTP clients are injected/reused or closed deterministically.
  • Production uses live data only and fails explicitly.

Private study

  • get_tasks, get_deadlines, and get_study_planner are available to the local assistant.
  • get_academic_status returns the richer term-aware contract without breaking existing callers.
  • ILIAS/Moodle partial failure returns usable results from the healthy source.
  • Dates are normalized only when reliable; ambiguous hints are preserved without invention.
  • Auth expiry is distinguishable from empty data.
  • Private tool definitions are absent from cloud requests by default.
  • Credentials/session data never enter prompts, tool arguments/results, traces, logs, caches, or remote wrapper calls.

Tests/docs

  • Tests cover schemas, routing, provider filtering, result bounds, empty/stale/auth-required/failed states, partial portal failure, date windows, deduplication, and cache invalidation.
  • Ported parsers have fixture tests based on the wrapper's known contracts.
  • Cloud tests prove private tools are omitted while public tools remain available.
  • Tool failures remain model-visible and do not abort unrelated calls.
  • Relevant docs/tool inventory and privacy behavior are updated.

Validation

Run from flutter_app/:

dart format --output=none --set-exit-if-changed lib test
flutter analyze
flutter test
flutter build web --release

Suggested delivery order

This issue may be implemented as focused PRs sharing the same capability contracts:

  1. capability result/policy/provider filtering + public tools
  2. local ILIAS/Moodle session and parsers + get_tasks/get_deadlines
  3. ALMA planner + richer academic status

Follow-up issues can cover public course discovery/seats/events/TIMMS and explicit-confirmation university actions.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions