Skip to content

feat: add ai support - #77

Open
HeZeBang wants to merge 19 commits into
masterfrom
dev/ai-toolkit
Open

feat: add ai support#77
HeZeBang wants to merge 19 commits into
masterfrom
dev/ai-toolkit

Conversation

@HeZeBang

@HeZeBang HeZeBang commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Note: this update depends on #78

HeZeBang and others added 9 commits July 22, 2026 16:15
- Add _loading guard to fetchAssignments() so concurrent calls return
  immediately instead of duplicating all deadline requests.
- Move enableAutoRefetch() into the boot async block, after all initial
  fetches finish, so listener-triggered fetches don't fire during startup.
Migrate the flutter_ai demo (../flutter_ai/demo) into techpie as a self-contained
scripted feature accessible from the home "应用" card. No real backend, no voice,
fully offline — everything runs against DemoChatProvider with bundled bytes.

- lib/pages/ai_demo/ — 6 files: AiDemoPage (entry), FeatureSections (6 element
  demos, voice dropped), GalleryScreen, DemoChatProvider (offline), ToolRunner,
  code highlighter (highlight package).
- lib/models/feature.dart — add ai_demo native entry in featureEntries.
- pubspec.yaml — path-depend on flutter_ai_core/client/elements (pure Dart,
  no OHOS override needed) + highlight; override core/client to path for
  transitive resolution.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the self-written chat rendering layer with the flutter_ai library
family (path deps from the sibling repo), so the conversation UI aligns
with the library's part-rendering pipeline (TextPart/ReasoningPart/
ToolCallPart/SourcePart/...) — groundwork for upcoming workflow/cite
controls.

- Wire flutter_ai_elements + flutter_ai_provider_anthropic + highlight;
  drop flutter_markdown.
- AiService wraps UseChatController + AnthropicProvider, exposing the
  controller for the UI to bind; keeps TechPie's persistence, config,
  and conversation-list ownership. DeepSeek's x-api-key auth is supported
  (verified), with baseUrl normalization to the /v1 root.
- Chat page uses library AiChat + AiPromptInput + AiErrorBanner; only the
  platform-adaptive top bar, config banner, and empty state stay TechPie's.
- StreamingMarkdownRenderer renders AiResponse (markdown + syntax-
  highlighted code blocks) live during streaming, not just on completion.
- Delete self-written ai_message_bubble.dart and ai_composer.dart.
- Migrate to the library's parts-based AiMessage; AiThread wraps it with
  TechPie's title/updatedAt metadata.
- Register AiThemeExtension on light/dark ThemeData so library widgets
  match the active color scheme.

flutter analyze: 0 errors/warnings. flutter test: 31 passing.

Co-Authored-By: Claude <noreply@anthropic.com>
Replace AiChat(autoScroll:false) with a custom _AutoScrollChat wrapper
around AiConversationView, driven by our own ScrollController. This keeps
the no-bottom-padding fix (no trailingSpace flicker) while restoring
scroll behavior:

- Scroll to end on send / new message, and follow streaming while the
  user stays near the bottom (don't yank back when they scroll up to read).
- Floating scroll-to-end button, shown only when not at the bottom;
  hides once at the end.
- Jump to the latest message on first open and on conversation switch
  (detected via first-message-id change, since the controller is a
  singleton that load()s a new transcript).

AiChat's own ScrollController is private, so driving scroll from outside
required dropping AiChat for the presentational AiConversationView.

Co-Authored-By: Claude <noreply@anthropic.com>
Wire the flutter_ai tool chain (ToolSpec + ToolRegistry + onToolCalls
agent loop) so the assistant can query real campus data via 4 read-only
tools:

- get_current_time: now / weekday / current teaching week
- get_semesters: available terms + current selection (eGate-gated)
- get_week_schedule: courses for a semester/week, grouped by weekday
  (eGate-gated; defaults to current semester + current week)
- get_assignments: upcoming assignments + exams across platforms
  (filterable by platform/kind)

Tools are declared as ToolSpecs in lib/services/ai_tools.dart; their
execute closures call ScheduleService / AssignmentService / DateTime.now()
and return JSON Maps. AiService injects tools + onToolCalls into the
UseChatController, enabling the automatic agent loop (multi-round,
maxSteps=8; validateToolArgs on). Tool executors auto-ensure data
(cache then network) and return a friendly error when eGate isn't bound.

Rendering: a custom messageBuilder on the chat view pairs each
ToolCallPart with its ToolResultPart across the whole transcript (the
agent loop lands results in a separate AiRole.tool message, which the
default bubble can't pair) — renders AiToolInvocation / AiToolGroup
cards that expand to show args + result.

System prompt updated to tell the model when to call each tool and to
surface eGate-binding errors to the user.

flutter analyze: 0 errors/warnings. flutter test: 31 passing.

Co-Authored-By: Claude <noreply@anthropic.com>
Adapt the AI tools to the unified session-node refactor:
- ThirdPartyAuthService.hasEgateBinding → hasCpdailyBinding (the eGate
  concept is now the cpdaily SessionNode). Tool guards updated; error
  copy unchanged in meaning.
- Resolve a formatting-only conflict in main.dart's boot fetch branch.

No other AI-tool changes needed: tools call the services' public APIs
(currentWeek / semesterInfo / courseTable / visibleAssignments /
fetchSemesters / fetchCourseTable / fetchAssignments), which are
unchanged by the node refactor — the session plumbing moved inside the
services.

flutter analyze: 0 errors/warnings. flutter test: 57 passing
(uniform-node's session/sync tests + AI tests).

Co-Authored-By: Claude <noreply@anthropic.com>
AI tools now read the per-semester storage cache first and only hit the
network on a miss (writing back to storage) or when the model passes
refresh=true. Fixes the old bug where tools read the in-memory course
table — which only holds the selected semester — so querying a past
semester returned nothing; and stops every AI query from firing live
API requests. Assignment fetch fan-out is factored into _fanOutFetches,
shared by the mutating and non-mutating paths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ol-loop

AiThread.fromJson filtered messages by text.isNotEmpty, which dropped
every AiRole.tool message (tool results have no text part) on each app
restart — leaving historical tool_use blocks unanswered, so Anthropic
rejected the whole conversation (400 'tool_use ids were found without
tool_result'). Keep any message with non-empty or non-text parts.

Also gate isStreaming/send on status.isBusy so the executingTools phase
counts as busy — previously a message sent while tools were running
landed on a transcript with unanswered tool calls.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
HeZeBang and others added 10 commits July 26, 2026 18:52
…into dev/ai-toolkit

# Conflicts:
#	lib/models/feature.dart
#	pubspec.lock
The pubspec referenced ../flutter_ai path deps that only exist on the dev
machine, and the checkout carries two fixes (OHOS haptics compile,
dangling tool-call settlement) not yet published to pub.dev. Vendor the
five packages under packages/flutter_ai/ and point all path deps and
overrides at them. Vendored code is excluded from techpie's analyzer.

Also dedupe the flutter_ai entries that the master merge left duplicated
in pubspec.yaml.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Trailing commas, const constructors, import ordering, unnecessary
imports, pubspec dependency sorting, and discarded_futures (await or
unawaited as appropriate).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The local fixes (OHOS haptics compile, dangling tool-call settlement)
are now pushed to the fork, so replace the vendored copies with git
dependencies pinned to commit 589a47d. Drops packages/flutter_ai and
its analyzer exclude.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ottom nav

Replace ScaffoldMessenger snackbars with an app-level AdaptiveFeedbackHost
overlay above the Navigator. The messenger rendered the same snackbar in
both scaffolds during route transitions, ghosting when pages with and
without the bottom nav gave it different offsets. The single banner now
animates its bottom clearance and corner radius in step with the page
transition: floating above the glass capsule on shell pages, near the
edge on pushed pages. Route depth is tracked via a NavigatorObserver
(dialogs excluded); showAdaptiveFeedback API unchanged. Theme-level
floating snackbar style kept for direct showSnackBar callers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ottom nav

Replace ScaffoldMessenger snackbars with an app-level AdaptiveFeedbackHost
overlay above the Navigator. The messenger rendered the same snackbar in
both scaffolds during route transitions, ghosting when pages with and
without the bottom nav gave it different offsets. The single banner now
animates its bottom clearance and corner radius in step with the page
transition: floating above the glass capsule on shell pages, near the
edge on pushed pages. Route depth is tracked via a NavigatorObserver
(dialogs excluded); showAdaptiveFeedback API unchanged. Theme-level
floating snackbar style kept for direct showSnackBar callers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Records that dev/ai-toolkit builds on the floating feedback banner
work, now split out onto dev/navbar-refactor so it can land on master
independently of the AI toolkit changes. No content change: the
feedback-host commit was already present in dev/ai-toolkit's history.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

1 participant