feat: add ai support - #77
Open
HeZeBang wants to merge 19 commits into
Open
Conversation
- 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>
…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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note: this update depends on #78