39 Commits

Author SHA1 Message Date
11aae1a353 feat(impression): introduce entity relation and impression model 2026-05-18 00:01:00 +08:00
e5dcb49028 chore: update gitignore 2026-05-17 21:07:21 +08:00
70a94d9c30 refactor(cognition): move context classes into context package 2026-05-16 21:57:29 +08:00
ef096e76b3 refactor(state): optimize StateValue building methods 2026-05-15 23:07:40 +08:00
ed743521ec chore: rename cognition core into context core 2026-05-15 16:16:44 +08:00
cb8ddfe4e2 docs: update README startup guide for PartnerCtl 2026-05-14 22:05:37 +08:00
756c0a12ad fix(partnerctl): include zh-CN locale in native image build 2026-05-14 20:41:53 +08:00
8a5b844a4a feat(partnerctl-init): add release download install option 2026-05-14 19:47:18 +08:00
github-actions[bot]
8d29ea4c9e chore(registry): update latest core release to release-core/0.9.0-preview 2026-05-14 09:08:43 +00:00
github-actions[bot]
4770eaf42f chore(registry): update latest buildable to buildable/0.9.0-preview 2026-05-14 09:07:33 +00:00
8bb266a1c3 chore(versioning): bump runtime, framework, and core to 0.9.0-preview 2026-05-14 17:06:06 +08:00
9054a9b4ad fix(onebot): correct interaction api dependency version 2026-05-13 13:04:45 +08:00
github-actions[bot]
c8d5f577a1 chore: update registry index 2026-05-13 03:35:47 +00:00
7c82c4aea5 chore: update onebot registry index version 2026-05-13 11:35:24 +08:00
5491ad1747 refactor(versioning): decouple module release versions
- bump parent and external module parent POMs to 1.0.0
- make runtime, interaction API, ctl, and external modules use explicit versions
- centralize internal dependency versions with dedicated properties
- keep framework and core on the shared runtime 0.5.0 line
- prepare partnerctl and module releases for independent versioning
2026-05-13 11:33:34 +08:00
1be6ed0198 chore: update idea settings 2026-05-13 10:19:55 +08:00
01bfc3ee18 feat(partnerctl-fetch): support fetching raw data via https proxy 2026-05-13 10:16:18 +08:00
2d45adf8c3 feat(partnerctl-module): load external modules from remote registry index 2026-05-12 23:34:35 +08:00
707fddda79 fix(release): checkout repository before creating ctl release 2026-05-11 18:16:40 +08:00
68589a21fc chore(release): add partnerctl release workflow 2026-05-11 15:18:15 +08:00
e8228d0cc1 fix(ci): make partnerctl native test workflow dispatchable 2026-05-11 11:33:00 +08:00
cc99e565f0 chore(ci): add partnerctl native build test workflow 2026-05-11 11:29:33 +08:00
github-actions[bot]
718271417d chore(registry): update latest core release to release-core/0.5.0 2026-05-11 03:15:42 +00:00
04ee38726b chore(release): use release-core tag workflow 2026-05-11 11:09:10 +08:00
github-actions[bot]
4a00e65868 chore(registry): update latest core release to rel-v0.5.0 2026-05-11 03:00:38 +00:00
6c3c08b5d5 chore: unify workflows name style 2026-05-11 10:24:29 +08:00
ef5a332f17 chore(release): add partner core release workflow 2026-05-11 10:05:43 +08:00
github-actions[bot]
9acabca40e chore(registry): update latest buildable to buildable/0.5.0 2026-05-10 15:28:36 +00:00
b654090a6e feat(git): add latest buildable update workflow 2026-05-10 23:18:07 +08:00
github-actions[bot]
c9a7343b30 chore: update registry index 2026-05-10 14:27:29 +00:00
6250b480e0 chore: add onebot adapter registry manifest 2026-05-10 22:27:13 +08:00
github-actions[bot]
63e8cc314a chore: update registry index 2026-05-10 14:22:35 +00:00
451f83e14d fix(module-registry): resolve manifest paths from registry in index update script 2026-05-10 22:22:12 +08:00
2b7e6718d9 fix(git): stage registry/index.json in update workflow 2026-05-10 22:10:41 +08:00
ed806c668a feat(git): add module index updating workflow 2026-05-10 22:08:47 +08:00
da381c3adf refactor(partnerctl-module): add version attribute to manifest 2026-05-10 21:40:20 +08:00
1cbff98b36 chore(git): remove legacy gitea sync workflow 2026-05-10 21:36:12 +08:00
95a30f147c chore: add initial module registry structure 2026-05-10 21:32:06 +08:00
0ea8dfc7d9 fix(core): correct logback log level fallback syntax (#1) 2026-05-09 14:51:28 +08:00
63 changed files with 1497 additions and 228 deletions

137
.github/workflows/release-core.yml vendored Normal file
View File

@@ -0,0 +1,137 @@
name: Release Partner Core
on:
workflow_dispatch:
inputs:
tag:
description: "Core release tag, for example release-core/0.5.0"
required: true
type: string
push:
tags:
- "release-core/*"
permissions:
contents: write
jobs:
release-core:
runs-on: ubuntu-latest
steps:
- name: Resolve release metadata
id: release
shell: bash
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TAG="${{ inputs.tag }}"
else
TAG="${GITHUB_REF_NAME}"
fi
VERSION="${TAG#release-core/}"
VERSION="${VERSION#v}"
ENCODED_TAG="${TAG//\//%2F}"
ASSET_NAME="partner-core-${VERSION}.jar"
ASSET_URL="https://github.com/slhaf/Partner/releases/download/${ENCODED_TAG}/${ASSET_NAME}"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "asset_name=${ASSET_NAME}" >> "$GITHUB_OUTPUT"
echo "asset_url=${ASSET_URL}" >> "$GITHUB_OUTPUT"
echo "Core release tag: ${TAG}"
echo "Core release version: ${VERSION}"
echo "Core release asset: ${ASSET_NAME}"
- name: Checkout release source
uses: actions/checkout@v4
with:
ref: ${{ steps.release.outputs.tag }}
- name: Set up Java 21
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"
- name: Build Partner Core
run: |
mvn -B -DskipTests=true -pl Partner-Core -am package
- name: Prepare release artifact
shell: bash
run: |
mkdir -p dist
SOURCE_JAR="Partner-Core/target/partner-core-${{ steps.release.outputs.version }}.jar"
if [ ! -f "$SOURCE_JAR" ]; then
echo "Expected artifact not found: $SOURCE_JAR"
echo "Available Partner-Core target files:"
find Partner-Core/target -maxdepth 1 -type f -name "*.jar" -print
exit 1
fi
cp "$SOURCE_JAR" "dist/${{ steps.release.outputs.asset_name }}"
ls -lh dist
- name: Create GitHub Release
shell: bash
run: |
if gh release view "${{ steps.release.outputs.tag }}" >/dev/null 2>&1; then
echo "Release ${{ steps.release.outputs.tag }} already exists."
exit 1
fi
gh release create "${{ steps.release.outputs.tag }}" \
"dist/${{ steps.release.outputs.asset_name }}" \
--title "Partner Core ${{ steps.release.outputs.version }}" \
--notes "Partner Core ${{ steps.release.outputs.version }}"
env:
GH_TOKEN: ${{ github.token }}
- name: Checkout master for registry update
uses: actions/checkout@v4
with:
ref: master
path: registry-worktree
- name: Update latestRelease
working-directory: registry-worktree
run: |
python3 - <<'PY'
import json
from pathlib import Path
version = "${{ steps.release.outputs.version }}"
url = "${{ steps.release.outputs.asset_url }}"
index_path = Path("registry/index.json")
index = json.loads(index_path.read_text(encoding="utf-8"))
index["partner"]["latestRelease"] = {
"url": url,
"version": version
}
index_path.write_text(
json.dumps(index, ensure_ascii=False, indent=2) + "\n",
encoding="utf-8"
)
PY
- name: Commit registry update
working-directory: registry-worktree
run: |
if git diff --quiet registry/index.json; then
echo "No latestRelease changes."
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add registry/index.json
git commit -m "chore(registry): update latest core release to ${{ steps.release.outputs.tag }}"
git push origin HEAD:master

161
.github/workflows/release-ctl.yml vendored Normal file
View File

@@ -0,0 +1,161 @@
name: Release PartnerCtl
on:
workflow_dispatch:
inputs:
tag:
description: "Ctl release tag, for example release-ctl/0.5.0"
required: true
type: string
push:
tags:
- "release-ctl/*"
permissions:
contents: write
jobs:
build:
name: Build ${{ matrix.platform }}
strategy:
fail-fast: false
matrix:
include:
- platform: linux-x64
runner: ubuntu-latest
binaryPath: PartnerCtl/target/partnerctl
assetSuffix: linux-x64
- platform: linux-arm64
runner: ubuntu-24.04-arm
binaryPath: PartnerCtl/target/partnerctl
assetSuffix: linux-arm64
- platform: windows-x64
runner: windows-latest
binaryPath: PartnerCtl/target/partnerctl.exe
assetSuffix: windows-x64.exe
runs-on: ${{ matrix.runner }}
steps:
- name: Resolve release metadata
id: release
shell: bash
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TAG="${{ inputs.tag }}"
else
TAG="${GITHUB_REF_NAME}"
fi
VERSION="${TAG#release-ctl/}"
VERSION="${VERSION#v}"
ASSET_NAME="partnerctl-${VERSION}-${{ matrix.assetSuffix }}"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "asset_name=${ASSET_NAME}" >> "$GITHUB_OUTPUT"
echo "Ctl release tag: ${TAG}"
echo "Ctl release version: ${VERSION}"
echo "Ctl release asset: ${ASSET_NAME}"
- name: Checkout release source
uses: actions/checkout@v4
with:
ref: ${{ steps.release.outputs.tag }}
- name: Set up GraalVM 21
uses: graalvm/setup-graalvm@v1
with:
distribution: graalvm-community
java-version: "21"
github-token: ${{ github.token }}
- name: Build partnerctl native image
run: mvn -B -DskipTests=true -pl PartnerCtl -am package native:compile
- name: Prepare release artifact
shell: bash
run: |
mkdir -p dist
BINARY_PATH="${{ matrix.binaryPath }}"
ASSET_PATH="dist/${{ steps.release.outputs.asset_name }}"
if [ ! -f "$BINARY_PATH" ]; then
echo "Expected native binary not found: $BINARY_PATH"
echo "Available PartnerCtl target files:"
find PartnerCtl/target -maxdepth 2 -type f -print
exit 1
fi
cp "$BINARY_PATH" "$ASSET_PATH"
if [[ "${{ matrix.platform }}" == linux-* ]]; then
chmod +x "$ASSET_PATH"
fi
ls -lh dist
- name: Upload release artifact
uses: actions/upload-artifact@v4
with:
name: ${{ steps.release.outputs.asset_name }}
path: dist/${{ steps.release.outputs.asset_name }}
if-no-files-found: error
retention-days: 1
release:
name: Create GitHub Release
needs: build
runs-on: ubuntu-latest
steps:
- name: Resolve release metadata
id: release
shell: bash
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TAG="${{ inputs.tag }}"
else
TAG="${GITHUB_REF_NAME}"
fi
VERSION="${TAG#release-ctl/}"
VERSION="${VERSION#v}"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "Ctl release tag: ${TAG}"
echo "Ctl release version: ${VERSION}"
- name: Checkout release source
uses: actions/checkout@v4
with:
ref: ${{ steps.release.outputs.tag }}
- name: Download release artifacts
uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- name: Show release artifacts
run: |
ls -lh dist
- name: Create GitHub Release
shell: bash
run: |
if gh release view "${{ steps.release.outputs.tag }}" >/dev/null 2>&1; then
echo "Release ${{ steps.release.outputs.tag }} already exists."
exit 1
fi
gh release create "${{ steps.release.outputs.tag }}" \
dist/* \
--title "PartnerCtl ${{ steps.release.outputs.version }}" \
--notes "PartnerCtl ${{ steps.release.outputs.version }}"
env:
GH_TOKEN: ${{ github.token }}

View File

@@ -1,36 +0,0 @@
name: Sync from Gitea
# 1. 给 GITHUB_TOKEN 开写权限
permissions:
contents: write
on:
schedule:
- cron: '*/30 * * * *'
workflow_dispatch:
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: 配置 Git 用户
run: |
git config --global user.name "Gitea Sync Bot"
git config --global user.email "slhafzjw@slhaf.work"
- name: 关闭全局 SSL 校验
run: git config --global http.sslVerify false
- name: Clone from Gitea (mirror)
run: |
git clone --mirror \
https://${{ secrets.GITEA_USER }}:${{ secrets.GITEA_TOKEN }}@${{ secrets.GITEA_URL }} \
gitea-mirror
- name: Push to GitHub
run: |
cd gitea-mirror
# 明确推到名为 "github" 的 remote
git remote add github \
https://${{ github.repository_owner }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
git push --mirror github

View File

@@ -0,0 +1,137 @@
name: Test PartnerCtl Native Build
on:
workflow_dispatch:
inputs:
platform:
description: "Target platform to test"
required: true
default: "windows-x64"
type: choice
options:
- windows-x64
- linux-x64
- linux-arm64
permissions:
contents: read
jobs:
test-linux-x64:
name: Test linux-x64
if: ${{ inputs.platform == 'linux-x64' }}
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up GraalVM 21
uses: graalvm/setup-graalvm@v1
with:
distribution: graalvm-community
java-version: "21"
github-token: ${{ github.token }}
- name: Build partnerctl native image
run: mvn -B -DskipTests=true -pl PartnerCtl -am package native:compile
- name: Check Linux output
shell: bash
run: |
if [ ! -f "PartnerCtl/target/partnerctl" ]; then
echo "Expected native binary not found: PartnerCtl/target/partnerctl"
echo "Available PartnerCtl target files:"
find PartnerCtl/target -maxdepth 2 -type f -print
exit 1
fi
chmod +x PartnerCtl/target/partnerctl
./PartnerCtl/target/partnerctl --help
- name: Upload test artifact
uses: actions/upload-artifact@v4
with:
name: partnerctl-linux-x64-test
path: PartnerCtl/target/partnerctl
if-no-files-found: error
retention-days: 1
test-linux-arm64:
name: Test linux-arm64
if: ${{ inputs.platform == 'linux-arm64' }}
runs-on: ubuntu-24.04-arm
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up GraalVM 21
uses: graalvm/setup-graalvm@v1
with:
distribution: graalvm-community
java-version: "21"
github-token: ${{ github.token }}
- name: Build partnerctl native image
run: mvn -B -DskipTests=true -pl PartnerCtl -am package native:compile
- name: Check Linux output
shell: bash
run: |
if [ ! -f "PartnerCtl/target/partnerctl" ]; then
echo "Expected native binary not found: PartnerCtl/target/partnerctl"
echo "Available PartnerCtl target files:"
find PartnerCtl/target -maxdepth 2 -type f -print
exit 1
fi
chmod +x PartnerCtl/target/partnerctl
./PartnerCtl/target/partnerctl --help
- name: Upload test artifact
uses: actions/upload-artifact@v4
with:
name: partnerctl-linux-arm64-test
path: PartnerCtl/target/partnerctl
if-no-files-found: error
retention-days: 1
test-windows-x64:
name: Test windows-x64
if: ${{ inputs.platform == 'windows-x64' }}
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up GraalVM 21
uses: graalvm/setup-graalvm@v1
with:
distribution: graalvm-community
java-version: "21"
github-token: ${{ github.token }}
- name: Build partnerctl native image
run: mvn -B -DskipTests=true -pl PartnerCtl -am package native:compile
- name: Check Windows output
shell: pwsh
run: |
if (!(Test-Path "PartnerCtl/target/partnerctl.exe")) {
Write-Host "Expected native binary not found: PartnerCtl/target/partnerctl.exe"
Write-Host "Available PartnerCtl target files:"
Get-ChildItem -Recurse PartnerCtl/target | Select-Object FullName
throw "partnerctl.exe not found"
}
.\PartnerCtl\target\partnerctl.exe --help
- name: Upload test artifact
uses: actions/upload-artifact@v4
with:
name: partnerctl-windows-x64-test
path: PartnerCtl/target/partnerctl.exe
if-no-files-found: error
retention-days: 1

View File

@@ -0,0 +1,70 @@
name: Update Latest Buildable
on:
workflow_dispatch:
inputs:
ref:
description: "Buildable ref, for example buildable/v0.5.0"
required: true
type: string
push:
tags:
- "buildable/*"
permissions:
contents: write
jobs:
update-latest-buildable:
runs-on: ubuntu-latest
steps:
- name: Resolve buildable ref
id: buildable
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "ref=${{ inputs.ref }}" >> "$GITHUB_OUTPUT"
else
echo "ref=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
fi
- name: Checkout master
uses: actions/checkout@v4
with:
ref: master
- name: Update latestBuildable
run: |
python3 - <<'PY'
import json
from pathlib import Path
ref = "${{ steps.buildable.outputs.ref }}"
index_path = Path("registry/index.json")
index = json.loads(index_path.read_text(encoding="utf-8"))
index["partner"]["latestBuildable"] = {
"url": "https://github.com/slhaf/Partner.git",
"ref": ref
}
index_path.write_text(
json.dumps(index, ensure_ascii=False, indent=2) + "\n",
encoding="utf-8"
)
PY
- name: Commit registry update
run: |
if git diff --quiet registry/index.json; then
echo "No latestBuildable changes."
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add registry/index.json
git commit -m "chore(registry): update latest buildable to ${{ steps.buildable.outputs.ref }}"
git push

View File

@@ -0,0 +1,38 @@
name: Update Module Index
on:
push:
branches:
- master
paths:
- "registry/modules/*.json"
- "registry/index.json"
- "scripts/update_module_index.py"
- "update-module-index.yml"
permissions:
contents: write
jobs:
update-registry-index:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Update module index
run: python3 scripts/update_module_index.py
- name: Commit updated index
run: |
if git diff --quiet registry/index.json; then
echo "No module index changes"
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add registry/index.json
git commit -m "chore: update registry index"
git push

1
.gitignore vendored
View File

@@ -61,3 +61,4 @@ build/
# Maven / build outputs # Maven / build outputs
dependency-reduced-pom.xml dependency-reduced-pom.xml
/.backup/

37
.idea/misc.xml generated
View File

@@ -1,28 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="EntryPointsManager"> <component name="EntryPointsManager">
<list size="21"> <list size="22">
<item index="0" class="java.lang.String" itemvalue="lombok.Data" /> <item index="0" class="java.lang.String" itemvalue="lombok.Data" />
<item index="1" class="java.lang.String" itemvalue="net.bytebuddy.implementation.bind.annotation.RuntimeType" /> <item index="1" class="java.lang.String" itemvalue="net.bytebuddy.implementation.bind.annotation.RuntimeType" />
<item index="2" class="java.lang.String" itemvalue="picocli.CommandLine.Command" /> <item index="2" class="java.lang.String" itemvalue="picocli.CommandLine.Command" />
<item index="3" class="java.lang.String" itemvalue="picocli.CommandLine.Mixin" /> <item index="3" class="java.lang.String" itemvalue="picocli.CommandLine.Mixin" />
<item index="4" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.Capability" /> <item index="4" class="java.lang.String" itemvalue="picocli.CommandLine.Option" />
<item index="5" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.CapabilityCore" /> <item index="5" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.Capability" />
<item index="6" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.CapabilityMethod" /> <item index="6" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.CapabilityCore" />
<item index="7" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.CoordinateManager" /> <item index="7" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.CapabilityMethod" />
<item index="8" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.Coordinated" /> <item index="8" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.CoordinateManager" />
<item index="9" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.component.annotation.Init" /> <item index="9" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.Coordinated" />
<item index="10" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AfterExecute" /> <item index="10" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.component.annotation.Init" />
<item index="11" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AgentRunningModule" /> <item index="11" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AfterExecute" />
<item index="12" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule" /> <item index="12" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AgentRunningModule" />
<item index="13" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.BeforeExecute" /> <item index="13" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule" />
<item index="14" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.Init" /> <item index="14" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.BeforeExecute" />
<item index="15" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CapabilityMethod" /> <item index="15" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.Init" />
<item index="16" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CoordinateManager" /> <item index="16" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CapabilityMethod" />
<item index="17" class="java.lang.String" itemvalue="work.slhaf.partner.api.register.capability.annotation.Capability" /> <item index="17" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CoordinateManager" />
<item index="18" class="java.lang.String" itemvalue="work.slhaf.partner.framework.agent.factory.capability.annotation.CapabilityCore" /> <item index="18" class="java.lang.String" itemvalue="work.slhaf.partner.api.register.capability.annotation.Capability" />
<item index="19" class="java.lang.String" itemvalue="work.slhaf.partner.framework.agent.factory.capability.annotation.CapabilityMethod" /> <item index="19" class="java.lang.String" itemvalue="work.slhaf.partner.framework.agent.factory.capability.annotation.CapabilityCore" />
<item index="20" class="java.lang.String" itemvalue="work.slhaf.partner.framework.agent.factory.component.annotation.AgentComponent" /> <item index="20" class="java.lang.String" itemvalue="work.slhaf.partner.framework.agent.factory.capability.annotation.CapabilityMethod" />
<item index="21" class="java.lang.String" itemvalue="work.slhaf.partner.framework.agent.factory.component.annotation.AgentComponent" />
</list> </list>
<writeAnnotations> <writeAnnotations>
<writeAnnotation name="work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability" /> <writeAnnotation name="work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability" />

View File

@@ -6,10 +6,11 @@
<parent> <parent>
<groupId>work.slhaf.partner</groupId> <groupId>work.slhaf.partner</groupId>
<artifactId>partner</artifactId> <artifactId>partner</artifactId>
<version>0.5.0</version> <version>1.0.0</version>
</parent> </parent>
<artifactId>partner-core</artifactId> <artifactId>partner-core</artifactId>
<version>0.9.0-preview</version>
<dependencies> <dependencies>
<dependency> <dependency>
@@ -20,7 +21,7 @@
<dependency> <dependency>
<groupId>work.slhaf.partner</groupId> <groupId>work.slhaf.partner</groupId>
<artifactId>partner-framework</artifactId> <artifactId>partner-framework</artifactId>
<version>0.5.0</version> <version>${partner.runtime.version}</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.nd4j/nd4j-api --> <!-- https://mvnrepository.com/artifact/org.nd4j/nd4j-api -->
<dependency> <dependency>

View File

@@ -50,70 +50,70 @@ final class ActionPoolStateCodec {
} }
private static StateValue.Obj encodeExecutableAction(ExecutableAction action) { private static StateValue.Obj encodeExecutableAction(ExecutableAction action) {
Map<String, StateValue> actionMap = new LinkedHashMap<>(); Map<String, Object> actionMap = new LinkedHashMap<>();
actionMap.put("kind", StateValue.str(action instanceof SchedulableExecutableAction ? "schedulable" : "immediate")); actionMap.put("kind", action instanceof SchedulableExecutableAction ? "schedulable" : "immediate");
actionMap.put("uuid", StateValue.str(action.getUuid())); actionMap.put("uuid", action.getUuid());
actionMap.put("source", StateValue.str(action.getSource())); actionMap.put("source", action.getSource());
actionMap.put("reason", StateValue.str(action.getReason())); actionMap.put("reason", action.getReason());
actionMap.put("description", StateValue.str(action.getDescription())); actionMap.put("description", action.getDescription());
actionMap.put("status", StateValue.str(action.getStatus().name())); actionMap.put("status", action.getStatus().name());
actionMap.put("tendency", StateValue.str(action.getTendency())); actionMap.put("tendency", action.getTendency());
actionMap.put("executing_stage", StateValue.num(action.getExecutingStage())); actionMap.put("executing_stage", action.getExecutingStage());
String result = resolveExecutableResult(action); String result = resolveExecutableResult(action);
if (result != null) { if (result != null) {
actionMap.put("result", StateValue.str(result)); actionMap.put("result", result);
} }
if (action instanceof SchedulableExecutableAction schedulableAction) { if (action instanceof SchedulableExecutableAction schedulableAction) {
actionMap.put("schedule_type", StateValue.str(schedulableAction.getScheduleType().name())); actionMap.put("schedule_type", schedulableAction.getScheduleType().name());
actionMap.put("schedule_content", StateValue.str(schedulableAction.getScheduleContent())); actionMap.put("schedule_content", schedulableAction.getScheduleContent());
actionMap.put("enabled", StateValue.bool(schedulableAction.getEnabled())); actionMap.put("enabled", schedulableAction.getEnabled());
actionMap.put("schedule_histories", StateValue.arr(encodeScheduleHistories(schedulableAction))); actionMap.put("schedule_histories", encodeScheduleHistories(schedulableAction));
} }
List<StateValue> chainStates = action.getActionChain().entrySet().stream() List<StateValue.Obj> chainStates = action.getActionChain().entrySet().stream()
.sorted(Map.Entry.comparingByKey()) .sorted(Map.Entry.comparingByKey())
.<StateValue>map(entry -> { .map(entry -> {
Map<String, StateValue> stageMap = new LinkedHashMap<>(); Map<String, Object> stageMap = new LinkedHashMap<>();
stageMap.put("stage", StateValue.num(entry.getKey())); stageMap.put("stage", entry.getKey());
String stageDescription = action.getStageDescriptions().get(entry.getKey()); String stageDescription = action.getStageDescriptions().get(entry.getKey());
if (stageDescription != null && !stageDescription.isBlank()) { if (stageDescription != null && !stageDescription.isBlank()) {
stageMap.put("description", StateValue.str(stageDescription)); stageMap.put("description", stageDescription);
} }
stageMap.put("actions", StateValue.arr(entry.getValue().stream() stageMap.put("actions", entry.getValue().stream()
.map(metaAction -> (StateValue) encodeMetaAction(metaAction)) .map(ActionPoolStateCodec::encodeMetaAction)
.toList())); .toList());
return StateValue.obj(stageMap); return StateValue.obj(stageMap);
}).toList(); }).toList();
actionMap.put("action_chain", StateValue.arr(chainStates)); actionMap.put("action_chain", chainStates);
actionMap.put("history", StateValue.arr(encodeHistoryStages(action.getHistory()))); actionMap.put("history", encodeHistoryStages(action.getHistory()));
return StateValue.obj(actionMap); return StateValue.obj(actionMap);
} }
private static StateValue.Obj encodeMetaAction(MetaAction metaAction) { private static StateValue.Obj encodeMetaAction(MetaAction metaAction) {
Map<String, StateValue> metaMap = new LinkedHashMap<>(); Map<String, Object> metaMap = new LinkedHashMap<>();
metaMap.put("name", StateValue.str(metaAction.getName())); metaMap.put("name", metaAction.getName());
metaMap.put("io", StateValue.bool(metaAction.getIo())); metaMap.put("io", metaAction.getIo());
if (metaAction.getLauncher() != null) { if (metaAction.getLauncher() != null) {
metaMap.put("launcher", StateValue.str(metaAction.getLauncher())); metaMap.put("launcher", metaAction.getLauncher());
} }
metaMap.put("type", StateValue.str(metaAction.getType().name())); metaMap.put("type", metaAction.getType().name());
metaMap.put("location", StateValue.str(metaAction.getLocation())); metaMap.put("location", metaAction.getLocation());
metaMap.put("params_json", StateValue.str(JSONObject.toJSONString(metaAction.getParams()))); metaMap.put("params_json", JSONObject.toJSONString(metaAction.getParams()));
metaMap.put("result_status", StateValue.str(metaAction.getResult().getStatus().name())); metaMap.put("result_status", metaAction.getResult().getStatus().name());
if (metaAction.getResult().getData() != null) { if (metaAction.getResult().getData() != null) {
metaMap.put("result_data", StateValue.str(metaAction.getResult().getData())); metaMap.put("result_data", metaAction.getResult().getData());
} }
return StateValue.obj(metaMap); return StateValue.obj(metaMap);
} }
private static StateValue.Obj encodeHistoryAction(HistoryAction historyAction) { private static StateValue.Obj encodeHistoryAction(HistoryAction historyAction) {
Map<String, StateValue> historyMap = new LinkedHashMap<>(); Map<String, Object> historyMap = new LinkedHashMap<>();
historyMap.put("action_key", StateValue.str(historyAction.actionKey())); historyMap.put("action_key", historyAction.actionKey());
historyMap.put("description", StateValue.str(historyAction.description())); historyMap.put("description", historyAction.description());
historyMap.put("result", StateValue.str(historyAction.result())); historyMap.put("result", historyAction.result());
return StateValue.obj(historyMap); return StateValue.obj(historyMap);
} }
@@ -288,26 +288,26 @@ final class ActionPoolStateCodec {
return restored; return restored;
} }
private static List<StateValue> encodeHistoryStages(Map<Integer, ? extends List<HistoryAction>> historyMap) { private static List<StateValue.Obj> encodeHistoryStages(Map<Integer, ? extends List<HistoryAction>> historyMap) {
return historyMap.entrySet().stream() return historyMap.entrySet().stream()
.sorted(Map.Entry.comparingByKey()) .sorted(Map.Entry.comparingByKey())
.<StateValue>map(entry -> { .map(entry -> {
Map<String, StateValue> stageMap = new LinkedHashMap<>(); Map<String, Object> stageMap = new LinkedHashMap<>();
stageMap.put("stage", StateValue.num(entry.getKey())); stageMap.put("stage", entry.getKey());
stageMap.put("actions", StateValue.arr(entry.getValue().stream() stageMap.put("actions", entry.getValue().stream()
.map(historyAction -> (StateValue) encodeHistoryAction(historyAction)) .map(ActionPoolStateCodec::encodeHistoryAction)
.toList())); .toList());
return StateValue.obj(stageMap); return StateValue.obj(stageMap);
}).toList(); }).toList();
} }
private static List<StateValue> encodeScheduleHistories(SchedulableExecutableAction schedulableAction) { private static List<StateValue.Obj> encodeScheduleHistories(SchedulableExecutableAction schedulableAction) {
return schedulableAction.getScheduleHistories().stream() return schedulableAction.getScheduleHistories().stream()
.<StateValue>map(scheduleHistory -> { .map(scheduleHistory -> {
Map<String, StateValue> historyMap = new LinkedHashMap<>(); Map<String, Object> historyMap = new LinkedHashMap<>();
historyMap.put("end_time", StateValue.str(scheduleHistory.getEndTime().toString())); historyMap.put("end_time", scheduleHistory.getEndTime().toString());
historyMap.put("result", StateValue.str(scheduleHistory.getResult())); historyMap.put("result", scheduleHistory.getResult());
historyMap.put("history", StateValue.arr(encodeHistoryStages(scheduleHistory.getHistory()))); historyMap.put("history", encodeHistoryStages(scheduleHistory.getHistory()));
return StateValue.obj(historyMap); return StateValue.obj(historyMap);
}) })
.toList(); .toList();

View File

@@ -1,6 +1,7 @@
package work.slhaf.partner.core.cognition; package work.slhaf.partner.core.cognition;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import work.slhaf.partner.core.cognition.context.ContextWorkspace;
import work.slhaf.partner.framework.agent.factory.capability.annotation.Capability; import work.slhaf.partner.framework.agent.factory.capability.annotation.Capability;
import work.slhaf.partner.framework.agent.model.pojo.Message; import work.slhaf.partner.framework.agent.model.pojo.Message;

View File

@@ -1,4 +1,4 @@
package work.slhaf.partner.core.cognition; package work.slhaf.partner.core.cognition.context;
import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
@@ -27,7 +27,7 @@ import java.util.concurrent.locks.ReentrantLock;
@Slf4j @Slf4j
@CapabilityCore(value = "cognition") @CapabilityCore(value = "cognition")
public class CognitionCore implements StateSerializable { public class ContextCore implements StateSerializable {
private static final String RECENT_CHAT_MESSAGE_NOTES = """ private static final String RECENT_CHAT_MESSAGE_NOTES = """
消息格式: 消息格式:
@@ -58,7 +58,7 @@ public class CognitionCore implements StateSerializable {
private final ContextWorkspace contextWorkspace = new ContextWorkspace(); private final ContextWorkspace contextWorkspace = new ContextWorkspace();
public CognitionCore() { public ContextCore() {
register(); register();
} }
@@ -200,13 +200,12 @@ public class CognitionCore implements StateSerializable {
public @NotNull State convert() { public @NotNull State convert() {
State state = new State(); State state = new State();
List<StateValue.Obj> convertedMessageList = chatMessages.stream().map(message -> { List<StateValue.Obj> convertedMessageList = chatMessages.stream()
Map<String, StateValue> convertedMap = Map.of( .map(message -> StateValue.obj(Map.of(
"role", StateValue.str(message.roleValue()), "role", message.roleValue(),
"content", StateValue.str(message.getContent()) "content", message.getContent()
); )))
return StateValue.obj(convertedMap); .toList();
}).toList();
state.append("chat_messages", StateValue.arr(convertedMessageList)); state.append("chat_messages", StateValue.arr(convertedMessageList));
return state; return state;

View File

@@ -1,4 +1,4 @@
package work.slhaf.partner.core.cognition package work.slhaf.partner.core.cognition.context
import com.alibaba.fastjson2.JSONObject import com.alibaba.fastjson2.JSONObject
import org.w3c.dom.Document import org.w3c.dom.Document

View File

@@ -1,4 +1,4 @@
package work.slhaf.partner.core.cognition package work.slhaf.partner.core.cognition.context
import org.w3c.dom.Document import org.w3c.dom.Document
import work.slhaf.partner.framework.agent.model.pojo.Message import work.slhaf.partner.framework.agent.model.pojo.Message

View File

@@ -0,0 +1,109 @@
package work.slhaf.partner.core.cognition.impression
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
class Entity @JvmOverloads constructor(
val uuid: String = UUID.randomUUID().toString(),
private val relations: MutableMap<String, MutableMap<String, Double>> = mutableMapOf(),
private val impressions: MutableMap<String, ImpressionData> = mutableMapOf()
) {
private val impressionLock = ReentrantLock()
private val relationLock = ReentrantLock()
@JvmOverloads
fun updateRelation(
target: String,
relation: String,
strength: Double = 1.0
) = relationLock.withLock {
relations.computeIfAbsent(target) { mutableMapOf() }[relation] = strength
}
@JvmOverloads
fun updateImpression(
impression: String,
newImpression: String? = null,
strength: Double = 1.0
): ImpressionData = impressionLock.withLock {
if (newImpression == null) {
impressions.computeIfAbsent(impression) { ImpressionData(strength) }
.also { it.confidence = strength }
} else {
impressions.remove(impression)
ImpressionData(strength).also {
impressions[newImpression] = it
}
}
}
fun removeImpression(impression: String) = impressionLock.withLock {
impressions.remove(impression)
}
@JvmOverloads
fun removeRelation(
target: String,
relation: String? = null
) = relationLock.withLock {
if (relation == null) {
relations.remove(target)
} else {
relations[target]?.remove(relation)
if (relations[target].isNullOrEmpty()) {
relations.remove(target)
}
}
}
fun showRelations(): Set<RelationView> = relationLock.withLock {
relations.map {
RelationView(
it.key,
it.value.toMap()
)
}.toSet()
}
fun showImpressions(embeddingModel: String): Set<ImpressionView> = impressionLock.withLock {
impressions.map {
ImpressionView(
it.key,
it.value.confidence,
it.value.getVector(embeddingModel)
)
}.toSet()
}
data class ImpressionData(
var confidence: Double
) {
private val vectors: ConcurrentHashMap<String, DoubleArray> = ConcurrentHashMap()
fun updateVector(
embeddingModel: String,
vector: DoubleArray
) {
vectors[embeddingModel] = vector
}
fun getVector(embeddingModel: String): DoubleArray? {
return vectors[embeddingModel]?.copyOf()
}
}
data class RelationView(
val target: String,
val relations: Map<String, Double>
)
@Suppress("ArrayInDataClass")
data class ImpressionView(
val impression: String,
val confidence: Double,
val vector: DoubleArray?
)
}

View File

@@ -175,8 +175,7 @@ public class MemoryCore implements StateSerializable {
State state = new State(); State state = new State();
state.append("memory_session_id", StateValue.str(memorySessionId)); state.append("memory_session_id", StateValue.str(memorySessionId));
List<StateValue.Str> unitOverview = memoryUnits.keySet().stream() List<String> unitOverview = memoryUnits.keySet().stream()
.map(StateValue::str)
.toList(); .toList();
state.append("memory_unit_uuid_set", StateValue.arr(unitOverview)); state.append("memory_unit_uuid_set", StateValue.arr(unitOverview));
return state; return state;

View File

@@ -95,25 +95,23 @@ public class MemoryUnit implements StateSerializable {
state.append("id", StateValue.str(id)); state.append("id", StateValue.str(id));
state.append("update_timestamp", StateValue.num(timestamp)); state.append("update_timestamp", StateValue.num(timestamp));
List<StateValue.Obj> convertedMessageList = conversationMessages.stream().map(message -> { List<StateValue.Obj> convertedMessageList = conversationMessages.stream()
Map<String, StateValue> convertedMap = Map.of( .map(message -> StateValue.obj(Map.of(
"role", StateValue.str(message.roleValue()), "role", message.roleValue(),
"content", StateValue.str(message.getContent()) "content", message.getContent()
); )))
return StateValue.obj(convertedMap); .toList();
}).toList();
state.append("conversation_messages", StateValue.arr(convertedMessageList)); state.append("conversation_messages", StateValue.arr(convertedMessageList));
List<StateValue.Obj> convertedSliceList = slices.stream().map(slice -> { List<StateValue.Obj> convertedSliceList = slices.stream()
Map<String, StateValue> convertedMap = Map.of( .map(slice -> StateValue.obj(Map.of(
"id", StateValue.str(slice.getId()), "id", slice.getId(),
"start_index", StateValue.num(slice.getStartIndex()), "start_index", slice.getStartIndex(),
"end_index", StateValue.num(slice.getEndIndex()), "end_index", slice.getEndIndex(),
"summary", StateValue.str(slice.getSummary()), "summary", slice.getSummary(),
"created_timestamp", StateValue.num(slice.getTimestamp()) "created_timestamp", slice.getTimestamp()
); )))
return StateValue.obj(convertedMap); .toList();
}).toList();
state.append("memory_slices", StateValue.arr(convertedSliceList)); state.append("memory_slices", StateValue.arr(convertedSliceList));
return state; return state;
} }

View File

@@ -6,9 +6,9 @@ import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import work.slhaf.partner.core.action.entity.MetaActionInfo; import work.slhaf.partner.core.action.entity.MetaActionInfo;
import work.slhaf.partner.core.cognition.BlockContent;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.ContextBlock; import work.slhaf.partner.core.cognition.context.BlockContent;
import work.slhaf.partner.core.cognition.context.ContextBlock;
import work.slhaf.partner.core.memory.MemoryCapability; import work.slhaf.partner.core.memory.MemoryCapability;
import work.slhaf.partner.core.memory.pojo.MemorySlice; import work.slhaf.partner.core.memory.pojo.MemorySlice;
import work.slhaf.partner.core.memory.pojo.MemoryUnit; import work.slhaf.partner.core.memory.pojo.MemoryUnit;

View File

@@ -12,7 +12,11 @@ import work.slhaf.partner.core.action.entity.MetaAction;
import work.slhaf.partner.core.action.entity.MetaActionInfo; import work.slhaf.partner.core.action.entity.MetaActionInfo;
import work.slhaf.partner.core.action.entity.intervention.InterventionType; import work.slhaf.partner.core.action.entity.intervention.InterventionType;
import work.slhaf.partner.core.action.entity.intervention.MetaIntervention; import work.slhaf.partner.core.action.entity.intervention.MetaIntervention;
import work.slhaf.partner.core.cognition.*; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.context.BlockContent;
import work.slhaf.partner.core.cognition.context.CommunicationBlockContent;
import work.slhaf.partner.core.cognition.context.ContextBlock;
import work.slhaf.partner.core.cognition.context.ContextWorkspace;
import work.slhaf.partner.framework.agent.exception.AgentRuntimeException; import work.slhaf.partner.framework.agent.exception.AgentRuntimeException;
import work.slhaf.partner.framework.agent.exception.ExceptionReporterHandler; import work.slhaf.partner.framework.agent.exception.ExceptionReporterHandler;
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability; import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;

View File

@@ -5,7 +5,7 @@ import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.ContextBlock; import work.slhaf.partner.core.cognition.context.ContextBlock;
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability; import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule; import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;
import work.slhaf.partner.framework.agent.model.ActivateModel; import work.slhaf.partner.framework.agent.model.ActivateModel;

View File

@@ -6,7 +6,7 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import work.slhaf.partner.core.action.ActionCapability; import work.slhaf.partner.core.action.ActionCapability;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.ContextBlock; import work.slhaf.partner.core.cognition.context.ContextBlock;
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability; import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule; import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;
import work.slhaf.partner.framework.agent.model.ActivateModel; import work.slhaf.partner.framework.agent.model.ActivateModel;

View File

@@ -6,9 +6,9 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import work.slhaf.partner.core.action.entity.*; import work.slhaf.partner.core.action.entity.*;
import work.slhaf.partner.core.action.entity.intervention.MetaIntervention; import work.slhaf.partner.core.action.entity.intervention.MetaIntervention;
import work.slhaf.partner.core.cognition.BlockContent; import work.slhaf.partner.core.cognition.context.BlockContent;
import work.slhaf.partner.core.cognition.ContextBlock; import work.slhaf.partner.core.cognition.context.ContextBlock;
import work.slhaf.partner.core.cognition.ContextWorkspace; import work.slhaf.partner.core.cognition.context.ContextWorkspace;
import work.slhaf.partner.module.StateHintContent; import work.slhaf.partner.module.StateHintContent;
import work.slhaf.partner.module.action.executor.entity.HistoryAction; import work.slhaf.partner.module.action.executor.entity.HistoryAction;

View File

@@ -6,7 +6,7 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import work.slhaf.partner.core.action.entity.MetaActionInfo; import work.slhaf.partner.core.action.entity.MetaActionInfo;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.ContextBlock; import work.slhaf.partner.core.cognition.context.ContextBlock;
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability; import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule; import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;
import work.slhaf.partner.framework.agent.model.ActivateModel; import work.slhaf.partner.framework.agent.model.ActivateModel;

View File

@@ -8,9 +8,9 @@ import org.w3c.dom.Element;
import work.slhaf.partner.core.action.ActionCapability; import work.slhaf.partner.core.action.ActionCapability;
import work.slhaf.partner.core.action.ActionCore; import work.slhaf.partner.core.action.ActionCore;
import work.slhaf.partner.core.action.entity.*; import work.slhaf.partner.core.action.entity.*;
import work.slhaf.partner.core.cognition.BlockContent;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.ContextBlock; import work.slhaf.partner.core.cognition.context.BlockContent;
import work.slhaf.partner.core.cognition.context.ContextBlock;
import work.slhaf.partner.framework.agent.exception.AgentRuntimeException; import work.slhaf.partner.framework.agent.exception.AgentRuntimeException;
import work.slhaf.partner.framework.agent.exception.ExceptionReporterHandler; import work.slhaf.partner.framework.agent.exception.ExceptionReporterHandler;
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability; import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;

View File

@@ -6,10 +6,10 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import work.slhaf.partner.core.action.ActionCapability; import work.slhaf.partner.core.action.ActionCapability;
import work.slhaf.partner.core.action.ActionCore; import work.slhaf.partner.core.action.ActionCore;
import work.slhaf.partner.core.cognition.BlockContent;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.ContextBlock; import work.slhaf.partner.core.cognition.context.BlockContent;
import work.slhaf.partner.core.cognition.ResolvedContext; import work.slhaf.partner.core.cognition.context.ContextBlock;
import work.slhaf.partner.core.cognition.context.ResolvedContext;
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability; import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule; import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;
import work.slhaf.partner.framework.agent.factory.component.annotation.Init; import work.slhaf.partner.framework.agent.factory.component.annotation.Init;

View File

@@ -2,7 +2,7 @@ package work.slhaf.partner.module.action.planner.extractor;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.ContextBlock; import work.slhaf.partner.core.cognition.context.ContextBlock;
import work.slhaf.partner.framework.agent.exception.AgentRuntimeException; import work.slhaf.partner.framework.agent.exception.AgentRuntimeException;
import work.slhaf.partner.framework.agent.exception.ModuleExecutionException; import work.slhaf.partner.framework.agent.exception.ModuleExecutionException;
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability; import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;

View File

@@ -3,8 +3,8 @@ package work.slhaf.partner.module
import org.w3c.dom.Document import org.w3c.dom.Document
import org.w3c.dom.Element import org.w3c.dom.Element
import work.slhaf.partner.common.base.Block import work.slhaf.partner.common.base.Block
import work.slhaf.partner.core.cognition.CommunicationBlockContent import work.slhaf.partner.core.cognition.context.CommunicationBlockContent
import work.slhaf.partner.core.cognition.ContextBlock import work.slhaf.partner.core.cognition.context.ContextBlock
import work.slhaf.partner.framework.agent.model.pojo.Message import work.slhaf.partner.framework.agent.model.pojo.Message
abstract class TaskBlock @JvmOverloads constructor( abstract class TaskBlock @JvmOverloads constructor(

View File

@@ -5,7 +5,11 @@ import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import work.slhaf.partner.core.cognition.*; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.context.BlockContent;
import work.slhaf.partner.core.cognition.context.CommunicationBlockContent;
import work.slhaf.partner.core.cognition.context.ContextBlock;
import work.slhaf.partner.core.cognition.context.ResolvedContext;
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability; import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule; import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;
import work.slhaf.partner.framework.agent.factory.component.annotation.Init; import work.slhaf.partner.framework.agent.factory.component.annotation.Init;

View File

@@ -9,9 +9,9 @@ import work.slhaf.partner.core.action.ActionCapability;
import work.slhaf.partner.core.action.ActionCore; import work.slhaf.partner.core.action.ActionCore;
import work.slhaf.partner.core.action.entity.Schedulable; import work.slhaf.partner.core.action.entity.Schedulable;
import work.slhaf.partner.core.action.entity.StateAction; import work.slhaf.partner.core.action.entity.StateAction;
import work.slhaf.partner.core.cognition.BlockContent;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.ContextBlock; import work.slhaf.partner.core.cognition.context.BlockContent;
import work.slhaf.partner.core.cognition.context.ContextBlock;
import work.slhaf.partner.core.memory.MemoryCapability; import work.slhaf.partner.core.memory.MemoryCapability;
import work.slhaf.partner.core.memory.pojo.MemorySlice; import work.slhaf.partner.core.memory.pojo.MemorySlice;
import work.slhaf.partner.core.memory.pojo.MemoryUnit; import work.slhaf.partner.core.memory.pojo.MemoryUnit;

View File

@@ -69,8 +69,8 @@ final class MemoryRuntimeStateCodec {
List<StateValue.Obj> dateIndexStates = dateIndex.entries().entrySet().stream() List<StateValue.Obj> dateIndexStates = dateIndex.entries().entrySet().stream()
.sorted(Map.Entry.comparingByKey()) .sorted(Map.Entry.comparingByKey())
.map(entry -> StateValue.obj(Map.of( .map(entry -> StateValue.obj(Map.of(
"date", StateValue.str(entry.getKey().toString()), "date", entry.getKey().toString(),
"refs", StateValue.arr(encodeSliceRefs(entry.getValue())) "refs", encodeSliceRefs(entry.getValue())
))) )))
.toList(); .toList();
state.append("date_index", StateValue.arr(dateIndexStates)); state.append("date_index", StateValue.arr(dateIndexStates));
@@ -82,8 +82,8 @@ final class MemoryRuntimeStateCodec {
TopicMemoryIndex.TopicTreeNode topicNode, TopicMemoryIndex.TopicTreeNode topicNode,
List<StateValue.Obj> topicStates) { List<StateValue.Obj> topicStates) {
topicStates.add(StateValue.obj(Map.of( topicStates.add(StateValue.obj(Map.of(
"topic_path", StateValue.str(path), "topic_path", path,
"bindings", StateValue.arr(encodeTopicBindings(topicNode.bindings())) "bindings", encodeTopicBindings(topicNode.bindings())
))); )));
for (Map.Entry<String, TopicMemoryIndex.TopicTreeNode> childEntry : topicNode.children().entrySet()) { for (Map.Entry<String, TopicMemoryIndex.TopicTreeNode> childEntry : topicNode.children().entrySet()) {
collectTopicStates(path + "->" + childEntry.getKey(), childEntry.getValue(), topicStates); collectTopicStates(path + "->" + childEntry.getKey(), childEntry.getValue(), topicStates);
@@ -93,18 +93,16 @@ final class MemoryRuntimeStateCodec {
private List<StateValue> encodeTopicBindings(List<TopicMemoryIndex.TopicBinding> bindings) { private List<StateValue> encodeTopicBindings(List<TopicMemoryIndex.TopicBinding> bindings) {
return bindings.stream() return bindings.stream()
.map(binding -> (StateValue) StateValue.obj(Map.of( .map(binding -> (StateValue) StateValue.obj(Map.of(
"unit_id", StateValue.str(binding.sliceRef().getUnitId()), "unit_id", binding.sliceRef().getUnitId(),
"slice_id", StateValue.str(binding.sliceRef().getSliceId()), "slice_id", binding.sliceRef().getSliceId(),
"timestamp", StateValue.num(binding.timestamp()), "timestamp", binding.timestamp(),
"activation_profile", StateValue.obj(Map.of( "activation_profile", StateValue.obj(Map.of(
"activation_weight", StateValue.num(binding.activationProfile().getActivationWeight()), "activation_weight", binding.activationProfile().getActivationWeight(),
"diffusion_weight", StateValue.num(binding.activationProfile().getDiffusionWeight()), "diffusion_weight", binding.activationProfile().getDiffusionWeight(),
"context_independence_weight", "context_independence_weight",
StateValue.num(binding.activationProfile().getContextIndependenceWeight()) binding.activationProfile().getContextIndependenceWeight()
)), )),
"related_topic_paths", StateValue.arr(binding.relatedTopicPaths().stream() "related_topic_paths", binding.relatedTopicPaths()
.map(StateValue::str)
.toList())
))) )))
.toList(); .toList();
} }
@@ -156,8 +154,8 @@ final class MemoryRuntimeStateCodec {
private List<StateValue> encodeSliceRefs(List<SliceRef> refs) { private List<StateValue> encodeSliceRefs(List<SliceRef> refs) {
return refs.stream() return refs.stream()
.map(ref -> (StateValue) StateValue.obj(Map.of( .map(ref -> (StateValue) StateValue.obj(Map.of(
"unit_id", StateValue.str(ref.getUnitId()), "unit_id", ref.getUnitId(),
"slice_id", StateValue.str(ref.getSliceId()) "slice_id", ref.getSliceId()
))) )))
.toList(); .toList();
} }

View File

@@ -7,9 +7,9 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import work.slhaf.partner.core.action.ActionCapability; import work.slhaf.partner.core.action.ActionCapability;
import work.slhaf.partner.core.action.ActionCore; import work.slhaf.partner.core.action.ActionCore;
import work.slhaf.partner.core.cognition.BlockContent;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.ContextBlock; import work.slhaf.partner.core.cognition.context.BlockContent;
import work.slhaf.partner.core.cognition.context.ContextBlock;
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability; import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule; import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;
import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule; import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule;

View File

@@ -7,7 +7,7 @@ import org.w3c.dom.Element;
import work.slhaf.partner.core.action.ActionCapability; import work.slhaf.partner.core.action.ActionCapability;
import work.slhaf.partner.core.action.ActionCore; import work.slhaf.partner.core.action.ActionCore;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.ContextBlock; import work.slhaf.partner.core.cognition.context.ContextBlock;
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability; import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule; import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;
import work.slhaf.partner.framework.agent.factory.component.annotation.Init; import work.slhaf.partner.framework.agent.factory.component.annotation.Init;

View File

@@ -5,7 +5,7 @@ import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.ContextBlock; import work.slhaf.partner.core.cognition.context.ContextBlock;
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability; import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule; import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;
import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule; import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule;

View File

@@ -3,10 +3,10 @@ package work.slhaf.partner.module.perceive;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import work.slhaf.partner.core.cognition.BlockContent;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.CommunicationBlockContent; import work.slhaf.partner.core.cognition.context.BlockContent;
import work.slhaf.partner.core.cognition.ContextBlock; import work.slhaf.partner.core.cognition.context.CommunicationBlockContent;
import work.slhaf.partner.core.cognition.context.ContextBlock;
import work.slhaf.partner.core.perceive.PerceiveCapability; import work.slhaf.partner.core.perceive.PerceiveCapability;
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability; import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule; import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;

View File

@@ -4,9 +4,9 @@ import kotlin.Unit;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import work.slhaf.partner.core.cognition.BlockContent;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.ContextBlock; import work.slhaf.partner.core.cognition.context.BlockContent;
import work.slhaf.partner.core.cognition.context.ContextBlock;
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability; import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule; import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;
import work.slhaf.partner.runtime.PartnerRunningFlowContext; import work.slhaf.partner.runtime.PartnerRunningFlowContext;

View File

@@ -3,9 +3,9 @@ package work.slhaf.partner.runtime.exception;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import work.slhaf.partner.core.cognition.BlockContent;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.ContextBlock; import work.slhaf.partner.core.cognition.context.BlockContent;
import work.slhaf.partner.core.cognition.context.ContextBlock;
import work.slhaf.partner.framework.agent.exception.AgentException; import work.slhaf.partner.framework.agent.exception.AgentException;
import work.slhaf.partner.framework.agent.exception.ExceptionReport; import work.slhaf.partner.framework.agent.exception.ExceptionReport;
import work.slhaf.partner.framework.agent.exception.ExceptionReporter; import work.slhaf.partner.framework.agent.exception.ExceptionReporter;

View File

@@ -1,7 +1,7 @@
<configuration> <configuration>
<property name="PARTNER_HOME" value="${PARTNER_HOME:-${user.home}/.partner}"/> <property name="PARTNER_HOME" value="${PARTNER_HOME:-${user.home}/.partner}"/>
<property name="PARTNER_LOG_DIR" value="${PARTNER_HOME}/state/trace/log"/> <property name="PARTNER_LOG_DIR" value="${PARTNER_HOME}/state/trace/log"/>
<property name="LOG_LEVEL" value="${PARTNER_LOG_LEVEL}:-${partner.log.level:-INFO}"/> <property name="LOG_LEVEL" value="${PARTNER_LOG_LEVEL:-${partner.log.level:-INFO}}"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder> <encoder>

View File

@@ -3,6 +3,8 @@ package work.slhaf.partner.core.cognition;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.api.io.TempDir;
import work.slhaf.partner.core.cognition.context.ContextBlock;
import work.slhaf.partner.core.cognition.context.ContextCore;
import work.slhaf.partner.framework.agent.model.pojo.Message; import work.slhaf.partner.framework.agent.model.pojo.Message;
import java.nio.file.Path; import java.nio.file.Path;
@@ -20,15 +22,15 @@ class CognitionCoreTest {
@Test @Test
void shouldRenderRecentChatMessagesWithWrapperAndNotes() { void shouldRenderRecentChatMessagesWithWrapperAndNotes() {
CognitionCore cognitionCore = new CognitionCore(); ContextCore contextCore = new ContextCore();
cognitionCore.getChatMessages().addAll(List.of( contextCore.getChatMessages().addAll(List.of(
new Message(Message.Character.USER, "[[USER]: user-1]: hello"), new Message(Message.Character.USER, "[[USER]: user-1]: hello"),
new Message(Message.Character.ASSISTANT, "[NOT_REPLIED]: wait"), new Message(Message.Character.ASSISTANT, "[NOT_REPLIED]: wait"),
new Message(Message.Character.ASSISTANT, "latest message") new Message(Message.Character.ASSISTANT, "latest message")
)); ));
cognitionCore.refreshRecentChatMessagesContext(); contextCore.refreshRecentChatMessagesContext();
String content = cognitionCore.contextWorkspace() String content = contextCore.contextWorkspace()
.resolve(List.of(ContextBlock.FocusedDomain.COMMUNICATION)) .resolve(List.of(ContextBlock.FocusedDomain.COMMUNICATION))
.encodeToMessage() .encodeToMessage()
.getContent(); .getContent();

View File

@@ -2,6 +2,9 @@ package work.slhaf.partner.core.cognition
import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import work.slhaf.partner.core.cognition.context.BlockContent
import work.slhaf.partner.core.cognition.context.ContextBlock
import work.slhaf.partner.core.cognition.context.ContextWorkspace
class ContextWorkspaceTest { class ContextWorkspaceTest {

View File

@@ -10,7 +10,7 @@ import work.slhaf.partner.core.action.ActionCore;
import work.slhaf.partner.core.action.entity.*; import work.slhaf.partner.core.action.entity.*;
import work.slhaf.partner.core.action.runner.RunnerClient; import work.slhaf.partner.core.action.runner.RunnerClient;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.ContextWorkspace; import work.slhaf.partner.core.cognition.context.ContextWorkspace;
import work.slhaf.partner.framework.agent.support.Result; import work.slhaf.partner.framework.agent.support.Result;
import work.slhaf.partner.module.action.executor.entity.ExtractorResult; import work.slhaf.partner.module.action.executor.entity.ExtractorResult;
import work.slhaf.partner.module.action.executor.entity.HistoryAction; import work.slhaf.partner.module.action.executor.entity.HistoryAction;

View File

@@ -3,7 +3,7 @@ package work.slhaf.partner.module.communication;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.ContextWorkspace; import work.slhaf.partner.core.cognition.context.ContextWorkspace;
import work.slhaf.partner.framework.agent.model.pojo.Message; import work.slhaf.partner.framework.agent.model.pojo.Message;
import work.slhaf.partner.runtime.PartnerRunningFlowContext; import work.slhaf.partner.runtime.PartnerRunningFlowContext;

View File

@@ -8,6 +8,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.api.io.TempDir;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import work.slhaf.partner.core.cognition.CognitionCapability; import work.slhaf.partner.core.cognition.CognitionCapability;
import work.slhaf.partner.core.cognition.context.ContextWorkspace;
import work.slhaf.partner.core.memory.MemoryCapability; import work.slhaf.partner.core.memory.MemoryCapability;
import work.slhaf.partner.core.memory.pojo.MemorySlice; import work.slhaf.partner.core.memory.pojo.MemorySlice;
import work.slhaf.partner.core.memory.pojo.MemoryUnit; import work.slhaf.partner.core.memory.pojo.MemoryUnit;
@@ -65,8 +66,8 @@ class MemoryRuntimeTest {
} }
@Override @Override
public work.slhaf.partner.core.cognition.ContextWorkspace contextWorkspace() { public ContextWorkspace contextWorkspace() {
return new work.slhaf.partner.core.cognition.ContextWorkspace(); return new ContextWorkspace();
} }
@Override @Override

View File

@@ -6,22 +6,23 @@
<parent> <parent>
<groupId>work.slhaf.partner</groupId> <groupId>work.slhaf.partner</groupId>
<artifactId>partner-external-modules</artifactId> <artifactId>partner-external-modules</artifactId>
<version>0.5.0</version> <version>1.0.0</version>
</parent> </parent>
<artifactId>partner-onebot-adapter</artifactId> <artifactId>partner-onebot-adapter</artifactId>
<version>1.0.0</version>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>work.slhaf.partner</groupId> <groupId>work.slhaf.partner</groupId>
<artifactId>partner-core</artifactId> <artifactId>partner-core</artifactId>
<version>${project.version}</version> <version>${partner.runtime.version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>work.slhaf.partner</groupId> <groupId>work.slhaf.partner</groupId>
<artifactId>partner-framework</artifactId> <artifactId>partner-framework</artifactId>
<version>${project.version}</version> <version>${partner.runtime.version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@@ -6,10 +6,11 @@
<parent> <parent>
<groupId>work.slhaf.partner</groupId> <groupId>work.slhaf.partner</groupId>
<artifactId>partner</artifactId> <artifactId>partner</artifactId>
<version>0.5.0</version> <version>1.0.0</version>
</parent> </parent>
<artifactId>partner-external-modules</artifactId> <artifactId>partner-external-modules</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<modules> <modules>

View File

@@ -6,10 +6,11 @@
<parent> <parent>
<groupId>work.slhaf.partner</groupId> <groupId>work.slhaf.partner</groupId>
<artifactId>partner</artifactId> <artifactId>partner</artifactId>
<version>0.5.0</version> <version>1.0.0</version>
</parent> </parent>
<artifactId>partner-framework</artifactId> <artifactId>partner-framework</artifactId>
<version>0.9.0-preview</version>
<dependencies> <dependencies>
<dependency> <dependency>
@@ -86,7 +87,7 @@
<dependency> <dependency>
<groupId>work.slhaf.partner</groupId> <groupId>work.slhaf.partner</groupId>
<artifactId>partner-interaction-api</artifactId> <artifactId>partner-interaction-api</artifactId>
<version>0.5.0</version> <version>${partner.interaction-api.version}</version>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@@ -148,10 +148,130 @@ sealed interface StateValue {
fun str(value: String) = Str(value) fun str(value: String) = Str(value)
@JvmStatic @JvmStatic
fun arr(value: List<StateValue>) = Arr(value) fun arr(value: List<*>): Arr {
val visiting = java.util.IdentityHashMap<Any, Unit>()
return Arr(convertList(value, visiting))
}
@JvmStatic @JvmStatic
fun obj(value: Map<String, StateValue>) = Obj(value) fun obj(value: Map<String, *>): Obj {
val visiting = java.util.IdentityHashMap<Any, Unit>()
return Obj(convertMap(value, visiting))
}
private fun convertValue(
value: Any?,
visiting: java.util.IdentityHashMap<Any, Unit>
): StateValue {
return when (value) {
null -> error("StateValue does not support null")
is StateValue -> normalizeStateValue(value, visiting)
is String -> Str(value)
is Number -> Num(value)
is Boolean -> Bool(value)
is List<*> -> Arr(convertList(value, visiting))
is Map<*, *> -> Obj(convertGenericMap(value, visiting))
else -> error("Unsupported state value type: ${value::class.qualifiedName}")
}
}
private fun normalizeStateValue(
value: StateValue,
visiting: java.util.IdentityHashMap<Any, Unit>
): StateValue {
return when (value) {
is Num -> value
is Bool -> value
is Str -> value
is Arr -> Arr(convertStateValueList(value.value, visiting))
is Obj -> Obj(convertStateValueMap(value.value, visiting))
}
}
private fun convertList(
value: List<*>,
visiting: java.util.IdentityHashMap<Any, Unit>
): List<StateValue> {
enterContainer(value, visiting)
try {
return value.map { convertValue(it, visiting) }
} finally {
leaveContainer(value, visiting)
}
}
private fun convertMap(
value: Map<String, *>,
visiting: java.util.IdentityHashMap<Any, Unit>
): Map<String, StateValue> {
enterContainer(value, visiting)
try {
return value.entries.associateTo(LinkedHashMap()) { (key, mapValue) ->
key to convertValue(mapValue, visiting)
}
} finally {
leaveContainer(value, visiting)
}
}
private fun convertGenericMap(
value: Map<*, *>,
visiting: java.util.IdentityHashMap<Any, Unit>
): Map<String, StateValue> {
enterContainer(value, visiting)
try {
return value.entries.associateTo(LinkedHashMap()) { (key, mapValue) ->
check(key is String) {
"StateValue object key must be String, but got: ${key?.let { it::class.qualifiedName }}"
}
key to convertValue(mapValue, visiting)
}
} finally {
leaveContainer(value, visiting)
}
}
private fun convertStateValueList(
value: List<StateValue>,
visiting: java.util.IdentityHashMap<Any, Unit>
): List<StateValue> {
enterContainer(value, visiting)
try {
return value.map { normalizeStateValue(it, visiting) }
} finally {
leaveContainer(value, visiting)
}
}
private fun convertStateValueMap(
value: Map<String, StateValue>,
visiting: java.util.IdentityHashMap<Any, Unit>
): Map<String, StateValue> {
enterContainer(value, visiting)
try {
return value.entries.associateTo(LinkedHashMap()) { (key, mapValue) ->
key to normalizeStateValue(mapValue, visiting)
}
} finally {
leaveContainer(value, visiting)
}
}
private fun enterContainer(
container: Any,
visiting: java.util.IdentityHashMap<Any, Unit>
) {
check(visiting.put(container, Unit) == null) {
"Circular reference detected while constructing StateValue"
}
}
private fun leaveContainer(
container: Any,
visiting: java.util.IdentityHashMap<Any, Unit>
) {
visiting.remove(container)
}
} }
} }

View File

@@ -0,0 +1,60 @@
package work.slhaf.partner.framework.agent.state
fun main() {
testNormalStateJson()
println()
testCircularReference()
}
private fun testNormalStateJson() {
val nestedMap = linkedMapOf(
"name" to "partner",
"enabled" to true,
"count" to 3,
"tags" to listOf("agent", "runtime", "state-center"),
"meta" to linkedMapOf(
"version" to "0.1.0",
"experimental" to false
)
)
val state = State()
state.append("root", StateValue.obj(nestedMap))
state.append(
"arr",
StateValue.arr(
listOf(
"hello",
123,
true,
linkedMapOf(
"nested" to "value"
)
)
)
)
println("=== normal state ===")
println(state.toString())
}
private fun testCircularReference() {
val cyclicMap = linkedMapOf<String, Any>()
cyclicMap["name"] = "cyclic"
cyclicMap["self"] = cyclicMap
println("=== circular reference ===")
try {
val state = State()
state.append("cyclic", StateValue.obj(cyclicMap))
// 如果前面没有抛错,这里再触发最终 JSON 输出
println(state.toString())
error("Expected circular reference detection, but no exception was thrown.")
} catch (e: IllegalStateException) {
println("circular reference detected as expected:")
println(e.message)
}
}

View File

@@ -6,10 +6,11 @@
<parent> <parent>
<groupId>work.slhaf.partner</groupId> <groupId>work.slhaf.partner</groupId>
<artifactId>partner</artifactId> <artifactId>partner</artifactId>
<version>0.5.0</version> <version>1.0.0</version>
</parent> </parent>
<artifactId>partner-interaction-api</artifactId> <artifactId>partner-interaction-api</artifactId>
<version>1.0.0</version>
<properties> <properties>
<maven.compiler.source>21</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>

View File

@@ -6,10 +6,11 @@
<parent> <parent>
<groupId>work.slhaf.partner</groupId> <groupId>work.slhaf.partner</groupId>
<artifactId>partner</artifactId> <artifactId>partner</artifactId>
<version>0.5.0</version> <version>1.0.0</version>
</parent> </parent>
<artifactId>partnerctl</artifactId> <artifactId>partnerctl</artifactId>
<version>1.0.1</version>
<properties> <properties>
<maven.compiler.source>21</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>
@@ -44,7 +45,7 @@
<dependency> <dependency>
<groupId>work.slhaf.partner</groupId> <groupId>work.slhaf.partner</groupId>
<artifactId>partner-interaction-api</artifactId> <artifactId>partner-interaction-api</artifactId>
<version>0.5.0</version> <version>${partner.interaction-api.version}</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
</dependencies> </dependencies>
@@ -122,6 +123,7 @@
<buildArg>-H:+ReportExceptionStackTraces</buildArg> <buildArg>-H:+ReportExceptionStackTraces</buildArg>
<buildArg>--initialize-at-build-time=kotlin.DeprecationLevel</buildArg> <buildArg>--initialize-at-build-time=kotlin.DeprecationLevel</buildArg>
<buildArg>-H:IncludeResourceBundles=i18n.messages</buildArg> <buildArg>-H:IncludeResourceBundles=i18n.messages</buildArg>
<buildArg>-H:IncludeLocales=zh-CN</buildArg>
</buildArgs> </buildArgs>
</configuration> </configuration>
</plugin> </plugin>

View File

@@ -2,13 +2,12 @@ package work.slhaf.partner.ctl.commands
import kotlinx.serialization.json.* import kotlinx.serialization.json.*
import picocli.CommandLine import picocli.CommandLine
import work.slhaf.partner.ctl.commands.InitCommand.InstallChoice.BUILD_FROM_SOURCE
import work.slhaf.partner.ctl.commands.InitCommand.InstallChoice.DOWNLOAD_JAR
import work.slhaf.partner.ctl.commands.data.GatewayConfig import work.slhaf.partner.ctl.commands.data.GatewayConfig
import work.slhaf.partner.ctl.commands.data.OpenAiCompatible import work.slhaf.partner.ctl.commands.data.OpenAiCompatible
import work.slhaf.partner.ctl.commands.data.ProviderConfig import work.slhaf.partner.ctl.commands.data.ProviderConfig
import work.slhaf.partner.ctl.commands.init.buildFromSource import work.slhaf.partner.ctl.commands.init.*
import work.slhaf.partner.ctl.commands.init.configureExternalGateway
import work.slhaf.partner.ctl.commands.init.configureOpenAiCompatible
import work.slhaf.partner.ctl.commands.init.configureWebSocketGateway
import work.slhaf.partner.ctl.i18n.I18n.text import work.slhaf.partner.ctl.i18n.I18n.text
import work.slhaf.partner.ctl.support.CommandInterrupted import work.slhaf.partner.ctl.support.CommandInterrupted
import work.slhaf.partner.ctl.support.inheritCommand import work.slhaf.partner.ctl.support.inheritCommand
@@ -176,11 +175,15 @@ class InitCommand : Runnable {
val installChoice = prompt.select( val installChoice = prompt.select(
label = text("init.install.method.label"), label = text("init.install.method.label"),
choices = listOf(Choice(text("init.install.method.buildFromSource"), InstallChoice.BUILD_FROM_SOURCE)) choices = listOf(
Choice(text("init.install.method.buildFromSource"), BUILD_FROM_SOURCE),
Choice(text("init.install.method.downloadFromRelease"), DOWNLOAD_JAR)
)
) )
when (installChoice) { when (installChoice) {
InstallChoice.BUILD_FROM_SOURCE -> buildFromSource(home, prompt) BUILD_FROM_SOURCE -> buildFromSource(home, prompt)
DOWNLOAD_JAR -> downloadFromRelease(home, prompt)
} }
} }
@@ -348,7 +351,8 @@ class InitCommand : Runnable {
} }
private enum class InstallChoice { private enum class InstallChoice {
BUILD_FROM_SOURCE BUILD_FROM_SOURCE,
DOWNLOAD_JAR
} }
private enum class ModelProviderChoice(val display: String) { private enum class ModelProviderChoice(val display: String) {

View File

@@ -64,7 +64,8 @@ fun configureExternalGateway(home: Path, prompt: Prompt, manifest: ModuleManifes
text("configure.gateway.external.details.buildCommand") to manifest.source.buildCommand.joinToString(" "), text("configure.gateway.external.details.buildCommand") to manifest.source.buildCommand.joinToString(" "),
text("configure.gateway.external.details.artifact") to "${manifest.source.artifactDirectory}/${manifest.source.artifactPattern}", text("configure.gateway.external.details.artifact") to "${manifest.source.artifactDirectory}/${manifest.source.artifactPattern}",
text("configure.gateway.external.details.installTarget") to manifest.install.target, text("configure.gateway.external.details.installTarget") to manifest.install.target,
text("configure.gateway.external.details.configTarget") to (manifest.config?.target ?: text("configure.gateway.external.details.noConfig")), text("configure.gateway.external.details.configTarget") to (manifest.config?.target
?: text("configure.gateway.external.details.noConfig")),
), ),
) )
@@ -138,18 +139,43 @@ private fun askField(prompt: Prompt, field: Field): JsonElement? {
} }
} }
@Suppress("KotlinConstantConditions")
private fun validateFieldValue(field: Field, value: String): String? { private fun validateFieldValue(field: Field, value: String): String? {
if (value.isBlank() && !field.required) return null if (value.isBlank() && !field.required) return null
return when (field.type) { return when (field.type) {
FieldType.STRING -> null FieldType.STRING -> null
FieldType.INT -> value.toIntOrNull()?.let { null } ?: text("configure.field.error.int", field.label) FieldType.INT -> {
FieldType.NUMBER -> value.toDoubleOrNull()?.let { null } ?: text("configure.field.error.number", field.label) if (value.toIntOrNull() == null) {
FieldType.BOOLEAN -> value.toBooleanStrictOrNull()?.let { null } ?: text("configure.field.error.boolean", field.label) text("configure.field.error.int", field.label)
FieldType.RAW_JSON -> runCatching { Json.parseToJsonElement(value) } } else {
.exceptionOrNull() null
?.let { text("configure.field.error.rawJson", field.label) } }
}
FieldType.NUMBER -> {
if (value.toDoubleOrNull() == null) {
text("configure.field.error.number", field.label)
} else {
null
}
}
FieldType.BOOLEAN -> {
if (value.toBooleanStrictOrNull() == null) {
text("configure.field.error.boolean", field.label)
} else {
null
}
}
FieldType.RAW_JSON -> {
val result = runCatching { Json.parseToJsonElement(value) }.exceptionOrNull()
if (result == null) {
text("configure.field.error.rawJson", field.label)
} else {
null
}
}
} }
} }

View File

@@ -1,15 +1,19 @@
package work.slhaf.partner.ctl.commands.init package work.slhaf.partner.ctl.commands.init
import work.slhaf.partner.ctl.i18n.I18n.text
import work.slhaf.partner.ctl.support.SourceBuildInstallSpec import work.slhaf.partner.ctl.support.SourceBuildInstallSpec
import work.slhaf.partner.ctl.support.buildAndInstallFromSource import work.slhaf.partner.ctl.support.buildAndInstallFromSource
import work.slhaf.partner.ctl.support.downloadTo
import work.slhaf.partner.ctl.support.registryIndex
import work.slhaf.partner.ctl.ui.Prompt import work.slhaf.partner.ctl.ui.Prompt
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
import kotlin.io.path.exists
import kotlin.io.path.isDirectory import kotlin.io.path.isDirectory
import kotlin.io.path.name import kotlin.io.path.name
private const val PARTNER_REPO_URL = "https://gitea.slhaf.work/slhaf/Partner.git" private const val PARTNER_REPO_URL = "https://github.com/slhaf/Partner.git"
fun buildFromSource(home: Path, prompt: Prompt) { fun buildFromSource(home: Path, prompt: Prompt) {
buildAndInstallFromSource( buildAndInstallFromSource(
@@ -40,3 +44,41 @@ private fun findLargestJar(directory: Path): Path? {
.orElse(null) .orElse(null)
} }
} }
fun downloadFromRelease(home: Path, prompt: Prompt) {
prompt.info(text("init.install.method.downloadFromRelease.startDownloading"))
val path = home.resolve("resources/partner-core.jar").toAbsolutePath().normalize()
downloadTo(registryIndex.partner.latestRelease.url, path) { downloaded, total ->
if (total != null && total > 0) {
val percent = downloaded * 100 / total
updateLine(
text(
"init.install.method.downloadFromRelease.progress.percent",
percent
)
)
} else {
updateLine(
text(
"init.install.method.downloadFromRelease.progress.size",
downloaded / 1024
)
)
}
}
finishLine(text("init.install.method.downloadFromRelease.done"))
if (!path.exists()) {
throw IllegalStateException("Unable to find downloaded partner release at $path")
}
prompt.success(text("init.install.method.downloadFromRelease.success"))
}
fun updateLine(text: String) {
print("\r\u001B[2K$text")
System.out.flush()
}
fun finishLine(text: String) {
updateLine(text)
println()
}

View File

@@ -1,10 +1,20 @@
package work.slhaf.partner.ctl.support package work.slhaf.partner.ctl.support
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
private const val registryUrl = "https://raw.githubusercontent.com/slhaf/Partner/refs/heads/master/registry"
private const val indexUrl = "$registryUrl/index.json"
val registryIndex = run {
Json.decodeFromString<RegistryIndex>(fetchText(indexUrl))
}
private fun loadModules(): Set<ModuleManifest> { private fun loadModules(): Set<ModuleManifest> {
// TODO: 待实现具体加载逻辑 return registryIndex.externalModules.map { indexItem ->
return emptySet() val manifestStr = fetchText("$registryUrl/${indexItem.registryRef}")
return@map Json.decodeFromString<ModuleManifest>(manifestStr)
}.toSet()
} }
fun loadAvailableGateway(): Set<ModuleManifest> { fun loadAvailableGateway(): Set<ModuleManifest> {
@@ -36,6 +46,7 @@ data class ModuleManifest(
/** Human-readable module description shown before installation. */ /** Human-readable module description shown before installation. */
val description: String = "", val description: String = "",
val version: String,
val source: Source, val source: Source,
val install: Install, val install: Install,
val config: Config? = null, val config: Config? = null,
@@ -91,3 +102,35 @@ enum class FieldType {
BOOLEAN, BOOLEAN,
RAW_JSON, RAW_JSON,
} }
@Serializable
data class RegistryIndex(
val partner: PartnerIndex,
val externalModules: List<ModulesIndexItem>
)
@Serializable
data class PartnerIndex(
val latestBuildable: Buildable,
val latestRelease: Release
) {
@Serializable
data class Buildable(
val url: String,
val ref: String
)
@Serializable
data class Release(
val url: String,
val version: String
)
}
@Serializable
data class ModulesIndexItem(
val name: String,
val version: String,
val withGateway: Boolean,
val registryRef: String
)

View File

@@ -0,0 +1,135 @@
package work.slhaf.partner.ctl.support
import java.io.IOException
import java.net.InetSocketAddress
import java.net.ProxySelector
import java.net.URI
import java.net.http.*
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardCopyOption
import java.time.Duration
import kotlin.io.path.isDirectory
private val httpClient: HttpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(20))
.followRedirects(HttpClient.Redirect.NORMAL)
.apply {
proxySelectorFromEnv()?.let(::proxy)
}
.build()
private fun proxySelectorFromEnv(): ProxySelector? {
val proxyText = System.getenv("HTTPS_PROXY")
?: System.getenv("https_proxy")
?: return null
val proxyUri = URI.create(proxyText)
val host = proxyUri.host
?: throw IllegalArgumentException("Invalid HTTPS_PROXY host: $proxyText")
val port = proxyUri.port
if (port == -1) {
throw IllegalArgumentException("HTTPS_PROXY must include port: $proxyText")
}
return ProxySelector.of(InetSocketAddress(host, port))
}
fun fetchText(url: String): String {
var lastError: Exception? = null
repeat(3) { attempt ->
try {
val request = HttpRequest.newBuilder()
.uri(URI.create(url))
.timeout(Duration.ofSeconds(60))
.header("User-Agent", "partnerctl")
.GET()
.build()
val response = httpClient.send(
request,
HttpResponse.BodyHandlers.ofString()
)
if (response.statusCode() !in 200..299) {
throw IOException("Failed to fetch $url: HTTP ${response.statusCode()}")
}
return response.body()
} catch (e: HttpTimeoutException) {
lastError = e
} catch (e: HttpConnectTimeoutException) {
lastError = e
} catch (e: IOException) {
lastError = e
} catch (e: InterruptedException) {
Thread.currentThread().interrupt()
throw IOException("Interrupted while fetching $url", e)
}
if (attempt < 2) {
Thread.sleep(500L * (attempt + 1))
}
}
throw IOException("Failed to fetch $url after retries", lastError)
}
fun downloadTo(
url: String,
targetPath: Path,
onProgress: (downloaded: Long, total: Long?) -> Unit = { _, _ -> }
) {
if (targetPath.isDirectory()) {
throw IllegalArgumentException("Target path must be a file")
}
val targetPath = targetPath.toAbsolutePath().normalize()
val targetFile = targetPath.toFile()
val temp = Files.createTempFile(
"${targetFile.name}-${System.currentTimeMillis()}", ".${targetFile.extension}.download"
)
try {
val request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.build()
val response = httpClient.send(
request,
HttpResponse.BodyHandlers.ofInputStream()
)
if (response.statusCode() !in 200..299) {
throw IllegalStateException("Failed to download from $url: HTTP ${response.statusCode()}")
}
val totalBytes = response.headers()
.firstValue("Content-Length")
.orElse(null)
?.toLongOrNull()
response.body().use { input ->
Files.newOutputStream(temp).use { output ->
val buffer = ByteArray(8192)
var downloaded = 0L
while (true) {
val read = input.read(buffer)
if (read < 0) break
output.write(buffer, 0, read)
downloaded += read
onProgress(downloaded, totalBytes)
}
}
}
Files.move(temp, targetPath, StandardCopyOption.REPLACE_EXISTING)
} catch (e: Exception) {
Files.deleteIfExists(temp)
throw e
}
}

View File

@@ -31,6 +31,12 @@ init.home.overwrite.refuseBroadDirectory=Refuse to overwrite suspiciously broad
init.install.section=Install Partner init.install.section=Install Partner
init.install.method.label=Choose an installation method init.install.method.label=Choose an installation method
init.install.method.buildFromSource=Build Partner from source init.install.method.buildFromSource=Build Partner from source
init.install.method.downloadFromRelease=Download Partner release
init.install.method.downloadFromRelease.startDownloading=Downloading Partner release...
init.install.method.downloadFromRelease.success=Partner release downloaded successfully.
init.install.method.downloadFromRelease.progress.percent=Downloading Partner release... {0}%
init.install.method.downloadFromRelease.progress.size=Downloading Partner release... {0} KB
init.install.method.downloadFromRelease.done=Downloading Partner release... Done
init.gateway.section=Configure Gateway init.gateway.section=Configure Gateway
init.gateway.select.label=Select gateway init.gateway.select.label=Select gateway
init.gateway.websocket.choice=WebSocket Gateway init.gateway.websocket.choice=WebSocket Gateway

View File

@@ -31,6 +31,12 @@ init.home.overwrite.refuseBroadDirectory=拒绝覆盖范围过大的目录:{0}
init.install.section=安装 Partner init.install.section=安装 Partner
init.install.method.label=选择安装方式 init.install.method.label=选择安装方式
init.install.method.buildFromSource=从源码构建 Partner init.install.method.buildFromSource=从源码构建 Partner
init.install.method.downloadFromRelease=下载 Partner 发布包
init.install.method.downloadFromRelease.startDownloading=正在下载 Partner 发布包...
init.install.method.downloadFromRelease.success=Partner 发布包下载完成。
init.install.method.downloadFromRelease.progress.percent=正在下载 Partner 发布包... {0}%
init.install.method.downloadFromRelease.progress.size=正在下载 Partner 发布包... {0} KB
init.install.method.downloadFromRelease.done=正在下载 Partner 发布包... 完成
init.gateway.section=配置网关 init.gateway.section=配置网关
init.gateway.select.label=选择网关 init.gateway.select.label=选择网关
init.gateway.websocket.choice=WebSocket Gateway init.gateway.websocket.choice=WebSocket Gateway

View File

@@ -0,0 +1,12 @@
package experimental
import kotlinx.serialization.json.Json
import work.slhaf.partner.ctl.support.RegistryIndex
import work.slhaf.partner.ctl.support.fetchText
fun main() {
val str = fetchText("https://raw.githubusercontent.com/slhaf/Partner/refs/heads/master/registry/index.json")
val index = Json.decodeFromString<RegistryIndex>(str)
println(index)
}

View File

@@ -25,12 +25,54 @@ Partner 分为 `Partner-Framework` 与 `Partner-Core` 两层。前者提供配
## 项目启动 ## 项目启动
**环境要求** ### 环境要求
**基础运行要求**
- JDK 21 - JDK 21
- Maven 3.x
### 手动准备环境并启动 **仅在从源码构建 Partner Runtime 或外部模块时需要**
- Maven 3.x
- Git
### 推荐方式PartnerCtl
`PartnerCtl` 用于完成 Partner 的首次初始化、运行时安装与启动管理。相比手动准备运行目录和配置文件,使用它可以更快完成最小可运行环境的搭建。
#### 初始化
```bash
partnerctl init
```
初始化流程会引导完成:
- 选择 `PARTNER_HOME`
- 安装 Partner Runtime
- 从源码构建
- 下载发布版 jar
- 配置 Gateway
- 配置模型 Provider
- 可选立即启动 Partner
#### 启动
如果初始化完成后未选择立即启动,可执行:
```bash
partnerctl run
```
如需后台运行:
```bash
partnerctl run -d
```
PartnerCtl 默认读取 `PARTNER_HOME` 指定的运行目录;若未设置,则使用 `~/.partner`
### 手动方式:从源码构建并启动
#### 克隆项目并构建 #### 克隆项目并构建
@@ -162,4 +204,3 @@ Partner/
## License ## License
暂未指定。 暂未指定。

View File

@@ -5,7 +5,7 @@
<groupId>work.slhaf.partner</groupId> <groupId>work.slhaf.partner</groupId>
<artifactId>partner</artifactId> <artifactId>partner</artifactId>
<version>0.5.0</version> <version>1.0.0</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<modules> <modules>
@@ -26,6 +26,9 @@
<!-- 推荐仓库默认不跳测试;本地需要时再 -DskipTests=true --> <!-- 推荐仓库默认不跳测试;本地需要时再 -DskipTests=true -->
<skipTests>false</skipTests> <skipTests>false</skipTests>
<partner.runtime.version>0.9.0-preview</partner.runtime.version>
<partner.interaction-api.version>1.0.0</partner.interaction-api.version>
</properties> </properties>
<dependencies> <dependencies>

20
registry/index.json Normal file
View File

@@ -0,0 +1,20 @@
{
"partner": {
"latestBuildable": {
"url": "https://github.com/slhaf/Partner.git",
"ref": "buildable/0.9.0-preview"
},
"latestRelease": {
"url": "https://github.com/slhaf/Partner/releases/download/release-core%2F0.9.0-preview/partner-core-0.9.0-preview.jar",
"version": "0.9.0-preview"
}
},
"externalModules": [
{
"name": "OneBot Adapter",
"version": "1.0.0",
"withGateway": true,
"registryRef": "modules/onebot-adapter.json"
}
]
}

View File

View File

@@ -0,0 +1,58 @@
{
"id": "onebot_channel",
"name": "OneBot Adapter",
"version": "1.0.0",
"withGateway": true,
"description": "OneBot v11 reverse WebSocket gateway adapter for Partner. It accepts reverse WebSocket connections from a OneBot implementation and converts private message events into Partner input events.",
"source": {
"url": "https://github.com/slhaf/Partner.git",
"sourceDirName": "Partner",
"buildCommand": [
"mvn",
"-B",
"-DskipTests=true",
"-pl",
"Partner-External-Modules/Partner-Onebot-Adapter",
"-am",
"package"
],
"artifactDirectory": "Partner-External-Modules/Partner-Onebot-Adapter/target",
"artifactPattern": "partner-onebot-adapter-*.jar"
},
"install": {
"target": "resources/module/partner-onebot-adapter.jar"
},
"config": {
"target": "config/gateway.json",
"fields": [
{
"name": "port",
"label": "OneBot reverse WebSocket server port",
"type": "INT",
"default": "29700",
"required": true
},
{
"name": "hostname",
"label": "OneBot reverse WebSocket server hostname",
"type": "STRING",
"default": "127.0.0.1",
"required": true
},
{
"name": "path",
"label": "OneBot reverse WebSocket path",
"type": "STRING",
"default": "/onebot/v11/ws",
"required": true
},
{
"name": "token",
"label": "OneBot access token",
"type": "STRING",
"default": "",
"required": false
}
]
}
}

View File

@@ -0,0 +1,59 @@
#!/usr/bin/env python3
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
INDEX_PATH = ROOT / "registry" / "index.json"
MODULES_DIR = ROOT / "registry" / "modules"
def load_json(path: Path) -> dict:
with path.open("r", encoding="utf-8") as f:
return json.load(f)
def write_json(path: Path, data: dict) -> None:
path.write_text(
json.dumps(data, ensure_ascii=False, indent=2) + "\n",
encoding="utf-8",
)
def build_external_modules() -> list[dict]:
entries = []
if not MODULES_DIR.exists():
return entries
for manifest_path in sorted(MODULES_DIR.glob("*.json")):
manifest = load_json(manifest_path)
# 这里按你当前 ModuleManifest 的完整文件结构取字段。
# version 如果 manifest 里暂时没有,可以先默认取 "0.5.0" 或直接要求 manifest 必须有。
name = manifest["name"]
version = manifest["version"]
with_gateway = manifest.get("withGateway", False)
rel_path = manifest_path.relative_to(ROOT / "registry").as_posix()
entries.append(
{
"name": name,
"version": version,
"withGateway": with_gateway,
"registryRef": rel_path,
}
)
return entries
def main() -> None:
index = load_json(INDEX_PATH)
index["externalModules"] = build_external_modules()
write_json(INDEX_PATH, index)
if __name__ == "__main__":
main()