Feat/Abstract Assistant into module with registry (#1020)

* Abstract Assistant into module with registry (fixes #1016)

- Add Assistant module with registry/factory (builtin, external)
- Assistant.for_chat(chat) routes by family.assistant_type
- Assistant.config_for(chat) delegates to Builtin for backward compat
- Assistant.available_types returns registered types
- Add Assistant::Base (Broadcastable, respond_to contract)
- Move current behavior to Assistant::Builtin (Provided + Configurable)
- Add Assistant::External stub for future OpenClaw/WebSocket
- Migration: add families.assistant_type (default builtin)
- Family: validate assistant_type inclusion
- Tests: for_chat routing, available_types, External stub, blank chat guard

* Fix RuboCop layout: indentation in Assistant module and tests

* Move new test methods above private so Minitest discovers them

* Clear thinking indicator in External#respond_to to avoid stuck UI

* Rebase onto upstream main: fix schema to avoid spurious diffs

- Rebase feature/abstract-assistant-1016 onto we-promise/main
- Rename migration to 20260218120001 to avoid duplicate version with backfill_crypto_subtype
- Regenerate schema from upstream + assistant_type only (keeps vector_store_id, realized_gain, etc.)
- PR schema diff now shows only assistant_type addition and version bump

---------

Co-authored-by: mkdev11 <jaysmth689+github@users.noreply.github.com>
This commit is contained in:
MkDev11
2026-02-23 04:38:58 -08:00
committed by GitHub
parent 0ddca461fc
commit 111d6839e0
8 changed files with 180 additions and 95 deletions

View File

@@ -176,6 +176,31 @@ class AssistantTest < ActiveSupport::TestCase
end
end
test "for_chat returns Builtin by default" do
assert_instance_of Assistant::Builtin, Assistant.for_chat(@chat)
end
test "available_types includes builtin and external" do
assert_includes Assistant.available_types, "builtin"
assert_includes Assistant.available_types, "external"
end
test "for_chat returns External when family assistant_type is external" do
@chat.user.family.update!(assistant_type: "external")
assistant = Assistant.for_chat(@chat)
assert_instance_of Assistant::External, assistant
assert_no_difference "AssistantMessage.count" do
assistant.respond_to(@message)
end
@chat.reload
assert @chat.error.present?
assert_includes @chat.error, "not yet implemented"
end
test "for_chat raises when chat is blank" do
assert_raises(Assistant::Error) { Assistant.for_chat(nil) }
end
private
def provider_function_request(id:, call_id:, function_name:, function_args:)
Provider::LlmConcept::ChatFunctionRequest.new(