From 159cde432e37943f53e2190c6864b79e25a1e47b Mon Sep 17 00:00:00 2001 From: nbschultz97 <126931519+nbschultz97@users.noreply.github.com> Date: Sat, 7 Mar 2026 09:49:10 -0700 Subject: [PATCH] Implement offline ATAK assistant plugin with marker create/edit ops --- CHANGELOG.md | 4 +- README.md | 15 +++-- .../example/tacticalapp/LocalChatActivity.kt | 48 +++++++++++++++ .../main/res/layout/activity_local_chat.xml | 19 +++++- llm/src/main/java/com/example/llm/LocalLlm.kt | 15 ++++- tak-plugin/build.gradle.kts | 3 + .../example/takplugin/TakOperationExecutor.kt | 44 ++++++++++++++ .../example/takplugin/TakOperationPlanner.kt | 53 +++++++++++++++++ .../com/example/takplugin/TakPluginService.kt | 58 ++++++++++++++++++- .../takplugin/TakOperationPlannerTest.kt | 26 +++++++++ 10 files changed, 274 insertions(+), 11 deletions(-) create mode 100644 tak-plugin/src/main/java/com/example/takplugin/TakOperationExecutor.kt create mode 100644 tak-plugin/src/main/java/com/example/takplugin/TakOperationPlanner.kt create mode 100644 tak-plugin/src/test/java/com/example/takplugin/TakOperationPlannerTest.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 37be459..131d61a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ # Changelog ## [Unreleased] -- Initial multi-module setup with LLM interface and TAK plugin stub. +- Replaced TAK plugin stub with an offline-first local assistant service that can parse and execute create/edit marker operations. +- Added local chat UI workflow that binds to the plugin service and returns actionable responses. +- Added unit tests for operation planning parser. diff --git a/README.md b/README.md index dff16bb..1734114 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,19 @@ # Tactical App -Multi-module Android project for an offline-first tactical AI assistant. +Multi-module Android project for an offline-first tactical AI assistant and ATAK plugin integration. ## Modules -- `app`: main Android application with basic home screen. -- `llm`: common LLM interface, router, and stub implementations. -- `tak-plugin`: CivTAK plugin stub for future CoT integration. +- `app`: main Android application with local assistant chat UI. +- `llm`: common LLM interface, router, and deterministic offline local model. +- `tak-plugin`: ATAK-facing assistant service that can parse restricted natural-language operations and execute local marker create/edit actions over CoT. +- `core`: CoT interoperability primitives and beacon support. ## Building Ensure JDK 17 and Android SDK are installed, then run: ``` ./gradlew assembleDebug ``` +(If `gradlew` is missing in your clone, use your local Gradle install with `gradle assembleDebug`.) ## Development A simple edge LLM stub server is available: @@ -19,4 +21,9 @@ A simple edge LLM stub server is available: ./scripts/dev/run_edge_stub.sh ``` +### Local ATAK assistant operation format +From the local chat UI you can issue: +- `create marker uid= lat= lon= type=` +- `edit marker uid= lat= lon= type=` + See `CONTRIBUTING.md` for details. diff --git a/app/src/main/java/com/example/tacticalapp/LocalChatActivity.kt b/app/src/main/java/com/example/tacticalapp/LocalChatActivity.kt index a7e1957..a98e844 100644 --- a/app/src/main/java/com/example/tacticalapp/LocalChatActivity.kt +++ b/app/src/main/java/com/example/tacticalapp/LocalChatActivity.kt @@ -1,15 +1,63 @@ package com.example.tacticalapp +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection import android.os.Bundle +import android.os.IBinder import androidx.appcompat.app.AppCompatActivity import com.example.tacticalapp.databinding.ActivityLocalChatBinding +import com.example.takplugin.TakPluginService class LocalChatActivity : AppCompatActivity() { private lateinit var binding: ActivityLocalChatBinding + private var pluginService: TakPluginService? = null + private var isBound: Boolean = false + + private val connection = object : ServiceConnection { + override fun onServiceConnected(name: ComponentName?, service: IBinder?) { + pluginService = (service as? TakPluginService.LocalBinder)?.service() + isBound = pluginService != null + } + + override fun onServiceDisconnected(name: ComponentName?) { + pluginService = null + isBound = false + } + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityLocalChatBinding.inflate(layoutInflater) setContentView(binding.root) + + binding.btnSend.setOnClickListener { + val prompt = binding.editPrompt.text?.toString().orEmpty() + val service = pluginService + if (prompt.isBlank() || service == null) { + binding.tvResponse.text = "Assistant unavailable. Ensure plugin service is bound." + return@setOnClickListener + } + binding.tvResponse.text = "Working..." + service.ask(prompt) { reply -> + runOnUiThread { + binding.tvResponse.text = reply.answer + } + } + } + } + + override fun onStart() { + super.onStart() + isBound = bindService(Intent(this, TakPluginService::class.java), connection, Context.BIND_AUTO_CREATE) + } + + override fun onStop() { + if (isBound) { + unbindService(connection) + isBound = false + } + super.onStop() } } diff --git a/app/src/main/res/layout/activity_local_chat.xml b/app/src/main/res/layout/activity_local_chat.xml index 8d9c0ab..1ab7ac3 100644 --- a/app/src/main/res/layout/activity_local_chat.xml +++ b/app/src/main/res/layout/activity_local_chat.xml @@ -5,9 +5,24 @@ android:orientation="vertical" android:padding="16dp"> + + +