assistant — AI assistant (Copilot)
Interview / defence / meeting copilot: hotkey help, context packing, multimodal Q&A, capture-safe floating window. Multiple instances allowed.
Declaration
json
{
"id": "community.assistant.interview_copilot",
"name": "AI 面试答题助手",
"version": "1.0.0",
"target_app_version": "0.2",
"type": "assistant",
"entrypoint": "copilot.py:InterviewCopilotPlugin",
"icon": "icon.svg",
"has_settings_tab": true,
"tab_id": "copilot",
"tab_title": "AI 面试助手",
"ui_extensions": {
"copilot_window": "Window.qml",
"style_bar_actions": "Action.qml",
"settings_view": "SettingsView.qml"
},
"config_schema": [
{ "key": "provider_preset", "label": "服务商预设", "type": "select",
"choices": ["小米 Token Plan", "DeepSeek", "Ollama 本地", "自定义"],
"presets": {
"Ollama 本地": { "base_url": "http://127.0.0.1:11434/v1", "model": "qwen2.5" }
} },
{ "key": "api_key", "label": "API Key", "type": "password", "is_secret": true }
]
}Shape
python
from transbox.plugins.base import BaseAssistantPlugin
class InterviewCopilot(BaseAssistantPlugin):
def action_ask(self, prompt="", image_data="", include_history=True, turns=5, **kwargs):
# 1. Fetch speaker-labelled structured history (ASR source text only, no machine translation, to avoid distortion)
recent_turns = self.host.get_recent_conversation(turns)
# 2. Slow network requests go to a background daemon thread; never block the Qt UI
self.run_in_background(self._do_stream, prompt, image_data, recent_turns)
def _do_stream(self, prompt, image_data, history):
# 3. Push streaming typewriter events to QML token by token
self.emit_event("stream_chunk", {"delta": "要点…", "done": False})
self.emit_event("stream_done", {"full_text": "完整答案…"})
def action_cancel(self, **kwargs):
...action_askis triggered from QML throughbridge.invokePluginActionAsync(pluginId, "ask", {...}), or by a hotkey.get_recent_conversation()returns{id, speaker, speaker_label, transcript, translation, time}, wherespeakerispeer/self. Feed onlytranscript(the source text) to the model — machine translation adds a second layer of distortion.- Use
self.emit_event()to push intermediate states to QML (see Event bus).
Mounting floating windows
json
"ui_extensions": {
"copilot_window": "Window.qml",
"style_bar_actions": "Action.qml"
}copilot_window: a standalone top-level frosted-glass interactive floating window (mounted on demand by the hostmain.qml). A mutually exclusive slot — only one assistant can use it at a time.style_bar_actions: insert custom action buttons into the caption bar's control row.
When you need several independent floating windows (not just the single copilot one), use Plugin windows SPI: host.create_plugin_window(...) supports multiple instances, positioning, click-through and capture exclusion.
Capture / screen-share exclusion (a hard requirement for interviews)
In your QML window:
qml
Component.onCompleted: bridge.setWindowExcludeFromCapture(this, true)This uses the native Windows SetWindowDisplayAffinity(0x00000011) — Tencent Meeting / Zoom / Teams and similar screen shares see the window as completely invisible. Returns False on non-Windows platforms.
Design aesthetics rules
- All QML must adapt to theme switching automatically via
ThemeTokensandPluggableWindowBackground. - Never use glyphs or emoji as icons — pure vector SVG only.
Reference implementation
extensions/community_ai_interview_copilot/.
Related
- Event bus
- Plugin windows
- Speech & feedback — toast / capture-exclusion APIs
- Custom settings view
