Merge pull request #4217 from janhq/dev

Hotfix 0.5.11 - Release cut
This commit is contained in:
Louis 2024-12-04 19:58:52 +07:00 committed by GitHub
commit 2ab306e955
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 177 additions and 500 deletions

View File

@ -34,18 +34,8 @@ jobs:
prerelease: false prerelease: false
generate_release_notes: true generate_release_notes: true
build-macos-x64: build-macos:
uses: ./.github/workflows/template-build-macos-x64.yml uses: ./.github/workflows/template-build-macos.yml
secrets: inherit
needs: [get-update-version]
with:
ref: ${{ github.ref }}
public_provider: github
new_version: ${{ needs.get-update-version.outputs.new_version }}
beta: true
build-macos-arm64:
uses: ./.github/workflows/template-build-macos-arm64.yml
secrets: inherit secrets: inherit
needs: [get-update-version] needs: [get-update-version]
with: with:
@ -74,53 +64,14 @@ jobs:
new_version: ${{ needs.get-update-version.outputs.new_version }} new_version: ${{ needs.get-update-version.outputs.new_version }}
beta: true beta: true
combine-beta-mac-yml: sync-temp-to-latest:
needs: [build-macos-x64, build-macos-arm64, create-draft-release, build-windows-x64, build-linux-x64] needs: [build-macos, create-draft-release, build-windows-x64, build-linux-x64]
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
contents: write contents: write
steps: steps:
- name: Getting the repo - name: Sync temp to latest
uses: actions/checkout@v3
- name: Download mac-x64 artifacts
uses: actions/download-artifact@v4
with:
name: beta-mac-x64
path: ./beta-mac-x64
- name: Download mac-arm artifacts
uses: actions/download-artifact@v4
with:
name: beta-mac-arm64
path: ./beta-mac-arm64
- name: 'Merge beta-mac.yml'
# unfortunately electron-builder doesn't understand that we have two different releases for mac-x64 and mac-arm, so we need to manually merge the latest files
# see https://github.com/electron-userland/electron-builder/issues/5592
run: | run: |
ls -la .
ls -la ./beta-mac-x64
ls -la ./beta-mac-arm64
ls -la ./electron
cp ./electron/merge-latest-ymls.js /tmp/merge-beta-ymls.js
npm install js-yaml --prefix /tmp
node /tmp/merge-beta-ymls.js ./beta-mac-x64/beta-mac.yml ./beta-mac-arm64/beta-mac.yml ./beta-mac.yml
cat ./beta-mac.yml
- name: Yet Another Upload Release Asset Action
uses: shogo82148/actions-upload-release-asset@v1.7.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-draft-release.outputs.upload_url }}
asset_path: ./beta-mac.yml
asset_name: beta-mac.yml
asset_content_type: text/yaml
overwrite: true
- name: Upload beta-mac.yml
run: |
aws s3 cp ./beta-mac.yml "s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/temp-beta/beta-mac.yml"
# sync temp-beta to beta by copy files that are different or new # sync temp-beta to beta by copy files that are different or new
aws s3 sync "s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/temp-beta/" "s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/beta/" aws s3 sync "s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/temp-beta/" "s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/beta/"
env: env:
@ -136,7 +87,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
noti-discord-and-update-url-readme: noti-discord-and-update-url-readme:
needs: [build-macos-x64, build-macos-arm64, create-draft-release, build-windows-x64, build-linux-x64, combine-beta-mac-yml] needs: [build-macos, create-draft-release, build-windows-x64, build-linux-x64, sync-temp-to-latest]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Set version to environment variable - name: Set version to environment variable
@ -149,8 +100,7 @@ jobs:
args: | args: |
Jan-beta App version {{ VERSION }}, has been released, use the following links to download the app with faster speed or visit the Github release page for more information: Jan-beta App version {{ VERSION }}, has been released, use the following links to download the app with faster speed or visit the Github release page for more information:
- Windows: https://delta.jan.ai/beta/jan-beta-win-x64-{{ VERSION }}.exe - Windows: https://delta.jan.ai/beta/jan-beta-win-x64-{{ VERSION }}.exe
- macOS Intel: https://delta.jan.ai/beta/jan-beta-mac-x64-{{ VERSION }}.dmg - macOS Universal: https://delta.jan.ai/beta/jan-beta-mac-universal-{{ VERSION }}.dmg
- macOS Apple Silicon: https://delta.jan.ai/beta/jan-beta-mac-arm64-{{ VERSION }}.dmg
- Linux Deb: https://delta.jan.ai/beta/jan-beta-linux-amd64-{{ VERSION }}.deb - Linux Deb: https://delta.jan.ai/beta/jan-beta-linux-amd64-{{ VERSION }}.deb
- Linux AppImage: https://delta.jan.ai/beta/jan-beta-linux-x86_64-{{ VERSION }}.AppImage - Linux AppImage: https://delta.jan.ai/beta/jan-beta-linux-x86_64-{{ VERSION }}.AppImage
- Github Release URL: https://github.com/janhq/jan/releases/tag/v{{ VERSION }} - Github Release URL: https://github.com/janhq/jan/releases/tag/v{{ VERSION }}

View File

@ -42,17 +42,8 @@ jobs:
get-update-version: get-update-version:
uses: ./.github/workflows/template-get-update-version.yml uses: ./.github/workflows/template-get-update-version.yml
build-macos-x64: build-macos:
uses: ./.github/workflows/template-build-macos-x64.yml uses: ./.github/workflows/template-build-macos.yml
needs: [get-update-version, set-public-provider]
secrets: inherit
with:
ref: ${{ needs.set-public-provider.outputs.ref }}
public_provider: ${{ needs.set-public-provider.outputs.public_provider }}
new_version: ${{ needs.get-update-version.outputs.new_version }}
build-macos-arm64:
uses: ./.github/workflows/template-build-macos-arm64.yml
needs: [get-update-version, set-public-provider] needs: [get-update-version, set-public-provider]
secrets: inherit secrets: inherit
with: with:
@ -79,42 +70,13 @@ jobs:
public_provider: ${{ needs.set-public-provider.outputs.public_provider }} public_provider: ${{ needs.set-public-provider.outputs.public_provider }}
new_version: ${{ needs.get-update-version.outputs.new_version }} new_version: ${{ needs.get-update-version.outputs.new_version }}
combine-latest-mac-yml: sync-temp-to-latest:
needs: [set-public-provider, build-macos-x64, build-macos-arm64, build-windows-x64, build-linux-x64] needs: [set-public-provider, build-windows-x64, build-linux-x64, build-macos]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Getting the repo - name: Sync temp to latest
uses: actions/checkout@v3
with:
ref: ${{ needs.set-public-provider.outputs.ref }}
- name: Download mac-x64 artifacts
uses: actions/download-artifact@v4
with:
name: latest-mac-x64
path: ./latest-mac-x64
- name: Download mac-arm artifacts
uses: actions/download-artifact@v4
with:
name: latest-mac-arm64
path: ./latest-mac-arm64
- name: 'Merge latest-mac.yml'
# unfortunately electron-builder doesn't understand that we have two different releases for mac-x64 and mac-arm, so we need to manually merge the latest files
# see https://github.com/electron-userland/electron-builder/issues/5592
run: |
ls -la .
ls -la ./latest-mac-x64
ls -la ./latest-mac-arm64
ls -la ./electron
cp ./electron/merge-latest-ymls.js /tmp/merge-latest-ymls.js
npm install js-yaml --prefix /tmp
node /tmp/merge-latest-ymls.js ./latest-mac-x64/latest-mac.yml ./latest-mac-arm64/latest-mac.yml ./latest-mac.yml
cat ./latest-mac.yml
- name: Upload latest-mac.yml
if: ${{ needs.set-public-provider.outputs.public_provider == 'aws-s3' }} if: ${{ needs.set-public-provider.outputs.public_provider == 'aws-s3' }}
run: | run: |
aws s3 cp ./latest-mac.yml "s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/temp-nightly/latest-mac.yml"
aws s3 sync s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/temp-nightly/ s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/nightly/ aws s3 sync s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/temp-nightly/ s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/nightly/
env: env:
AWS_ACCESS_KEY_ID: ${{ secrets.DELTA_AWS_ACCESS_KEY_ID }} AWS_ACCESS_KEY_ID: ${{ secrets.DELTA_AWS_ACCESS_KEY_ID }}
@ -123,7 +85,7 @@ jobs:
AWS_EC2_METADATA_DISABLED: "true" AWS_EC2_METADATA_DISABLED: "true"
noti-discord-nightly-and-update-url-readme: noti-discord-nightly-and-update-url-readme:
needs: [build-macos-x64, build-macos-arm64, build-windows-x64, build-linux-x64, get-update-version, set-public-provider, combine-latest-mac-yml] needs: [build-macos, build-windows-x64, build-linux-x64, get-update-version, set-public-provider, sync-temp-to-latest]
secrets: inherit secrets: inherit
if: github.event_name == 'schedule' if: github.event_name == 'schedule'
uses: ./.github/workflows/template-noti-discord-and-update-url-readme.yml uses: ./.github/workflows/template-noti-discord-and-update-url-readme.yml
@ -134,7 +96,7 @@ jobs:
new_version: ${{ needs.get-update-version.outputs.new_version }} new_version: ${{ needs.get-update-version.outputs.new_version }}
noti-discord-pre-release-and-update-url-readme: noti-discord-pre-release-and-update-url-readme:
needs: [build-macos-x64, build-macos-arm64, build-windows-x64, build-linux-x64, get-update-version, set-public-provider, combine-latest-mac-yml] needs: [build-macos, build-windows-x64, build-linux-x64, get-update-version, set-public-provider, sync-temp-to-latest]
secrets: inherit secrets: inherit
if: github.event_name == 'push' if: github.event_name == 'push'
uses: ./.github/workflows/template-noti-discord-and-update-url-readme.yml uses: ./.github/workflows/template-noti-discord-and-update-url-readme.yml
@ -145,7 +107,7 @@ jobs:
new_version: ${{ needs.get-update-version.outputs.new_version }} new_version: ${{ needs.get-update-version.outputs.new_version }}
noti-discord-manual-and-update-url-readme: noti-discord-manual-and-update-url-readme:
needs: [build-macos-x64, build-macos-arm64, build-windows-x64, build-linux-x64, get-update-version, set-public-provider, combine-latest-mac-yml] needs: [build-macos, build-windows-x64, build-linux-x64, get-update-version, set-public-provider, sync-temp-to-latest]
secrets: inherit secrets: inherit
if: github.event_name == 'workflow_dispatch' && github.event.inputs.public_provider == 'aws-s3' if: github.event_name == 'workflow_dispatch' && github.event.inputs.public_provider == 'aws-s3'
uses: ./.github/workflows/template-noti-discord-and-update-url-readme.yml uses: ./.github/workflows/template-noti-discord-and-update-url-readme.yml

View File

@ -33,17 +33,8 @@ jobs:
draft: true draft: true
prerelease: false prerelease: false
build-macos-x64: build-macos:
uses: ./.github/workflows/template-build-macos-x64.yml uses: ./.github/workflows/template-build-macos.yml
secrets: inherit
needs: [get-update-version]
with:
ref: ${{ github.ref }}
public_provider: github
new_version: ${{ needs.get-update-version.outputs.new_version }}
build-macos-arm64:
uses: ./.github/workflows/template-build-macos-arm64.yml
secrets: inherit secrets: inherit
needs: [get-update-version] needs: [get-update-version]
with: with:
@ -69,52 +60,8 @@ jobs:
public_provider: github public_provider: github
new_version: ${{ needs.get-update-version.outputs.new_version }} new_version: ${{ needs.get-update-version.outputs.new_version }}
combine-latest-mac-yml:
needs: [build-macos-x64, build-macos-arm64, create-draft-release]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Getting the repo
uses: actions/checkout@v3
- name: Download mac-x64 artifacts
uses: actions/download-artifact@v4
with:
name: latest-mac-x64
path: ./latest-mac-x64
- name: Download mac-arm artifacts
uses: actions/download-artifact@v4
with:
name: latest-mac-arm64
path: ./latest-mac-arm64
- name: 'Merge latest-mac.yml'
# unfortunately electron-builder doesn't understand that we have two different releases for mac-x64 and mac-arm, so we need to manually merge the latest files
# see https://github.com/electron-userland/electron-builder/issues/5592
run: |
ls -la .
ls -la ./latest-mac-x64
ls -la ./latest-mac-arm64
ls -la ./electron
cp ./electron/merge-latest-ymls.js /tmp/merge-latest-ymls.js
npm install js-yaml --prefix /tmp
node /tmp/merge-latest-ymls.js ./latest-mac-x64/latest-mac.yml ./latest-mac-arm64/latest-mac.yml ./latest-mac.yml
cat ./latest-mac.yml
- name: Yet Another Upload Release Asset Action
uses: shogo82148/actions-upload-release-asset@v1.7.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-draft-release.outputs.upload_url }}
asset_path: ./latest-mac.yml
asset_name: latest-mac.yml
asset_content_type: text/yaml
overwrite: true
update_release_draft: update_release_draft:
needs: [build-macos-x64, build-macos-arm64, build-windows-x64, build-linux-x64, combine-latest-mac-yml] needs: [build-macos, build-windows-x64, build-linux-x64]
permissions: permissions:
# write permission is required to create a github release # write permission is required to create a github release
contents: write contents: write

View File

@ -1,210 +0,0 @@
name: build-macos
on:
workflow_call:
inputs:
ref:
required: true
type: string
default: 'refs/heads/main'
public_provider:
required: true
type: string
default: none
description: 'none: build only, github: build and publish to github, aws s3: build and publish to aws s3'
new_version:
required: true
type: string
default: ''
aws_s3_prefix:
required: false
type: string
default: '/latest/'
beta:
required: false
type: boolean
default: false
secrets:
DELTA_AWS_S3_BUCKET_NAME:
required: false
DELTA_AWS_ACCESS_KEY_ID:
required: false
DELTA_AWS_SECRET_ACCESS_KEY:
required: false
CODE_SIGN_P12_BASE64:
required: false
CODE_SIGN_P12_PASSWORD:
required: false
APPLE_ID:
required: false
APPLE_APP_SPECIFIC_PASSWORD:
required: false
DEVELOPER_ID:
required: false
jobs:
build-macos:
runs-on: macos-13
environment: production
permissions:
contents: write
steps:
- name: Getting the repo
uses: actions/checkout@v3
with:
ref: ${{ inputs.ref }}
- name: Installing node
uses: actions/setup-node@v1
with:
node-version: 20
- name: Install jq
uses: dcarbone/install-jq-action@v2.0.1
- name: Update app version based on latest release tag with build number
if: inputs.public_provider != 'github'
run: |
echo "Version: ${{ inputs.new_version }}"
# Update the version in electron/package.json
jq --arg version "${{ inputs.new_version }}" '.version = $version' electron/package.json > /tmp/package.json
mv /tmp/package.json electron/package.json
jq --arg version "${{ inputs.new_version }}" '.version = $version' web/package.json > /tmp/package.json
mv /tmp/package.json web/package.json
jq '.build.publish = [{"provider": "generic", "url": "https://delta.jan.ai/nightly", "channel": "latest"}, {"provider": "s3", "acl": null, "bucket": "${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}", "region": "${{ secrets.DELTA_AWS_REGION}}", "path": "temp-nightly", "channel": "latest"}]' electron/package.json > /tmp/package.json
mv /tmp/package.json electron/package.json
jq --arg teamid "${{ secrets.APPLE_TEAM_ID }}" '.build.mac.notarize.teamId = $teamid' electron/package.json > /tmp/package.json
mv /tmp/package.json electron/package.json
cat electron/package.json
chmod +x .github/scripts/rename-app.sh
.github/scripts/rename-app.sh ./electron/package.json nightly
chmod +x .github/scripts/rename-workspace.sh
.github/scripts/rename-workspace.sh ./package.json nightly
echo "------------------------"
cat ./electron/package.json
echo "------------------------"
- name: Change App Name for beta version
if: inputs.beta == true
shell: bash
run: |
chmod +x .github/scripts/rename-app.sh
.github/scripts/rename-app.sh ./electron/package.json beta
chmod +x .github/scripts/rename-workspace.sh
.github/scripts/rename-workspace.sh ./package.json beta
echo "------------------------"
cat ./electron/package.json
echo "------------------------"
cat ./package.json
jq '.build.publish = [{"provider": "generic", "url": "https://delta.jan.ai/beta", "channel": "beta"}, {"provider": "github", "owner": "janhq", "repo": "jan", "channel": "beta"}, {"provider": "s3", "acl": null, "bucket": "${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}", "region": "${{ secrets.DELTA_AWS_REGION}}", "path": "temp-beta", "channel": "beta"}]' electron/package.json > /tmp/package.json
mv /tmp/package.json electron/package.json
cat electron/package.json
- name: Update app version base on tag
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && inputs.public_provider == 'github'
run: |
jq --arg version "${VERSION_TAG#v}" '.version = $version' electron/package.json > /tmp/package.json
mv /tmp/package.json electron/package.json
jq --arg version "${VERSION_TAG#v}" '.version = $version' web/package.json > /tmp/package.json
mv /tmp/package.json web/package.json
jq --arg teamid "${{ secrets.APPLE_TEAM_ID }}" '.build.mac.notarize.teamId = $teamid' electron/package.json > /tmp/package.json
mv /tmp/package.json electron/package.json
cat electron/package.json
env:
VERSION_TAG: ${{ inputs.new_version }}
- name: Get Cer for code signing
run: base64 -d <<< "$CODE_SIGN_P12_BASE64" > /tmp/codesign.p12
shell: bash
env:
CODE_SIGN_P12_BASE64: ${{ secrets.CODE_SIGN_P12_BASE64 }}
- uses: apple-actions/import-codesign-certs@v2
continue-on-error: true
with:
p12-file-base64: ${{ secrets.CODE_SIGN_P12_BASE64 }}
p12-password: ${{ secrets.CODE_SIGN_P12_PASSWORD }}
- name: Build and publish app to aws s3 r2 or github artifactory
if: inputs.public_provider != 'github'
run: |
# check public_provider is true or not
echo "public_provider is ${{ inputs.public_provider }}"
if [ "${{ inputs.public_provider }}" == "none" ]; then
make build
else
make build-and-publish
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_LINK: "/tmp/codesign.p12"
CSC_KEY_PASSWORD: ${{ secrets.CODE_SIGN_P12_PASSWORD }}
CSC_IDENTITY_AUTO_DISCOVERY: "true"
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APP_PATH: "."
DEVELOPER_ID: ${{ secrets.DEVELOPER_ID }}
AWS_ACCESS_KEY_ID: ${{ secrets.DELTA_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.DELTA_AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: auto
AWS_EC2_METADATA_DISABLED: "true"
AWS_MAX_ATTEMPTS: "5"
- name: Build and publish app to github
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && inputs.public_provider == 'github' && inputs.beta == false
run: |
make build-and-publish
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_LINK: "/tmp/codesign.p12"
CSC_KEY_PASSWORD: ${{ secrets.CODE_SIGN_P12_PASSWORD }}
CSC_IDENTITY_AUTO_DISCOVERY: "true"
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APP_PATH: "."
DEVELOPER_ID: ${{ secrets.DEVELOPER_ID }}
ANALYTICS_ID: ${{ secrets.JAN_APP_UMAMI_PROJECT_API_KEY }}
ANALYTICS_HOST: ${{ secrets.JAN_APP_UMAMI_URL }}
- name: Build and publish app to github
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && inputs.public_provider == 'github' && inputs.beta == true
run: |
make build-and-publish
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_LINK: "/tmp/codesign.p12"
CSC_KEY_PASSWORD: ${{ secrets.CODE_SIGN_P12_PASSWORD }}
CSC_IDENTITY_AUTO_DISCOVERY: "true"
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APP_PATH: "."
DEVELOPER_ID: ${{ secrets.DEVELOPER_ID }}
AWS_ACCESS_KEY_ID: ${{ secrets.DELTA_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.DELTA_AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: auto
AWS_EC2_METADATA_DISABLED: "true"
AWS_MAX_ATTEMPTS: "5"
- name: Upload Artifact
if: inputs.public_provider != 'github'
uses: actions/upload-artifact@v4
with:
name: jan-mac-x64-${{ inputs.new_version }}
path: ./electron/dist/*.dmg
- name: Upload Artifact
if: inputs.beta == false
uses: actions/upload-artifact@v4
with:
name: latest-mac-x64
path: ./electron/dist/latest-mac.yml
- name: Upload Artifact
if: inputs.beta == true
uses: actions/upload-artifact@v4
with:
name: beta-mac-x64
path: ./electron/dist/beta-mac.yml

View File

@ -192,19 +192,5 @@ jobs:
if: inputs.public_provider != 'github' if: inputs.public_provider != 'github'
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: jan-mac-arm64-${{ inputs.new_version }} name: jan-mac-universal-${{ inputs.new_version }}
path: ./electron/dist/*.dmg path: ./electron/dist/*.dmg
- name: Upload Artifact
if: inputs.beta == false
uses: actions/upload-artifact@v4
with:
name: latest-mac-arm64
path: ./electron/dist/latest-mac.yml
- name: Upload Artifact
if: inputs.beta == true
uses: actions/upload-artifact@v4
with:
name: beta-mac-arm64
path: ./electron/dist/beta-mac.yml

View File

@ -48,8 +48,7 @@ jobs:
args: | args: |
Jan App ${{ inputs.build_reason }} build artifact version {{ VERSION }}: Jan App ${{ inputs.build_reason }} build artifact version {{ VERSION }}:
- Windows: https://delta.jan.ai/nightly/jan-nightly-win-x64-{{ VERSION }}.exe - Windows: https://delta.jan.ai/nightly/jan-nightly-win-x64-{{ VERSION }}.exe
- macOS Intel: https://delta.jan.ai/nightly/jan-nightly-mac-x64-{{ VERSION }}.dmg - macOS Universal: https://delta.jan.ai/nightly/jan-nightly-mac-universal-{{ VERSION }}.dmg
- macOS Apple Silicon: https://delta.jan.ai/nightly/jan-nightly-mac-arm64-{{ VERSION }}.dmg
- Linux Deb: https://delta.jan.ai/nightly/jan-nightly-linux-amd64-{{ VERSION }}.deb - Linux Deb: https://delta.jan.ai/nightly/jan-nightly-linux-amd64-{{ VERSION }}.deb
- Linux AppImage: https://delta.jan.ai/nightly/jan-nightly-linux-x86_64-{{ VERSION }}.AppImage - Linux AppImage: https://delta.jan.ai/nightly/jan-nightly-linux-x86_64-{{ VERSION }}.AppImage
- Github action run: https://github.com/janhq/jan/actions/runs/{{ GITHUB_RUN_ID }} - Github action run: https://github.com/janhq/jan/actions/runs/{{ GITHUB_RUN_ID }}

View File

@ -133,6 +133,7 @@ else ifeq ($(shell uname -s),Linux)
find . -name ".turbo" -type d -exec rm -rf '{}' + find . -name ".turbo" -type d -exec rm -rf '{}' +
find . -name "packake-lock.json" -type f -exec rm -rf '{}' + find . -name "packake-lock.json" -type f -exec rm -rf '{}' +
find . -name "yarn.lock" -type f -exec rm -rf '{}' + find . -name "yarn.lock" -type f -exec rm -rf '{}' +
find . -name "package-lock.json" -type f -exec rm -rf '{}' +
rm -rf ./pre-install/*.tgz rm -rf ./pre-install/*.tgz
rm -rf ./extensions/*/*.tgz rm -rf ./extensions/*/*.tgz
rm -rf ./electron/pre-install/*.tgz rm -rf ./electron/pre-install/*.tgz
@ -145,7 +146,7 @@ else
find . -name "build" -type d -exec rm -rf '{}' + find . -name "build" -type d -exec rm -rf '{}' +
find . -name "out" -type d -exec rm -rf '{}' + find . -name "out" -type d -exec rm -rf '{}' +
find . -name ".turbo" -type d -exec rm -rf '{}' + find . -name ".turbo" -type d -exec rm -rf '{}' +
find . -name "packake-lock.json" -type f -exec rm -rf '{}' + find . -name "package-lock.json" -type f -exec rm -rf '{}' +
find . -name "yarn.lock" -type f -exec rm -rf '{}' + find . -name "yarn.lock" -type f -exec rm -rf '{}' +
rm -rf ./pre-install/*.tgz rm -rf ./pre-install/*.tgz
rm -rf ./extensions/*/*.tgz rm -rf ./extensions/*/*.tgz

View File

@ -132,9 +132,6 @@ export abstract class OAIEngine extends AIEngine {
events.emit(MessageEvent.OnMessageUpdate, message) events.emit(MessageEvent.OnMessageUpdate, message)
}, },
error: async (err: any) => { error: async (err: any) => {
console.debug('inference url: ', this.inferenceUrl)
console.debug('header: ', header)
console.error(`Inference error:`, JSON.stringify(err))
if (this.isCancelled || message.content.length) { if (this.isCancelled || message.content.length) {
message.status = MessageStatus.Stopped message.status = MessageStatus.Stopped
events.emit(MessageEvent.OnMessageUpdate, message) events.emit(MessageEvent.OnMessageUpdate, message)

View File

@ -77,6 +77,11 @@ export function requestInference(
const toParse = cachedLines + line const toParse = cachedLines + line
if (!line.includes('data: [DONE]')) { if (!line.includes('data: [DONE]')) {
const data = JSON.parse(toParse.replace('data: ', '')) const data = JSON.parse(toParse.replace('data: ', ''))
if ('error' in data) {
subscriber.error(data.error)
subscriber.complete()
return
}
content += data.choices[0]?.delta?.content ?? '' content += data.choices[0]?.delta?.content ?? ''
if (content.startsWith('assistant: ')) { if (content.startsWith('assistant: ')) {
content = content.replace('assistant: ', '') content = content.replace('assistant: ', '')

View File

@ -81,7 +81,7 @@ These errors relate to the local API server's functionality.
#### Error Types #### Error Types
| Error Code | Cause | Solution | | Error Code | Cause | Solution |
|------------|----------------------------------------|--------------------------------------------------------------| |------------|----------------------------------------|--------------------------------------------------------------|
| API-1 | Port 3928 is currently unavailable. | [Local API Server Guide](/docs/local-api#step-1-set-the-local-server) | | API-1 | Port 39291 is currently unavailable. | [Local API Server Guide](/docs/local-api#step-1-set-the-local-server) |
### 8. Extensions and Integration Errors ### 8. Extensions and Integration Errors

View File

@ -368,23 +368,23 @@ When you start a chat with a model and encounter a Something's Amiss error, here
4. Confirm your V/RAM accessibility, mainly if using virtual RAM. 4. Confirm your V/RAM accessibility, mainly if using virtual RAM.
5. Nvidia GPU users should download [CUDA](https://developer.nvidia.com/cuda-downloads). 5. Nvidia GPU users should download [CUDA](https://developer.nvidia.com/cuda-downloads).
6. Linux users, ensure your system meets the requirements of gcc 11, g++ 11, cpp 11, or higher. Refer to this [link](#troubleshooting-nvidia-gpu) for details. 6. Linux users, ensure your system meets the requirements of gcc 11, g++ 11, cpp 11, or higher. Refer to this [link](#troubleshooting-nvidia-gpu) for details.
7. You might use the wrong port when you [check the app logs](#how-to-get-error-logs) and encounter the Bind address failed at 127.0.0.1:3928 error. To check the port status, try using the `netstat` command, like the following: 7. You might use the wrong port when you [check the app logs](#how-to-get-error-logs) and encounter the Bind address failed at 127.0.0.1:39291 error. To check the port status, try using the `netstat` command, like the following:
<Tabs items={['Mac', 'Windows', 'Linux']}> <Tabs items={['Mac', 'Windows', 'Linux']}>
<Tabs.Tab > <Tabs.Tab >
```bash ```bash
netstat -an | grep 3928 netstat -an | grep 39291
``` ```
</Tabs.Tab> </Tabs.Tab>
<Tabs.Tab > <Tabs.Tab >
```bash ```bash
netstat -ano | find "3928" netstat -ano | find "39291"
tasklist /fi "PID eq 3928" tasklist /fi "PID eq 39291"
``` ```
</Tabs.Tab> </Tabs.Tab>
<Tabs.Tab > <Tabs.Tab >
```bash ```bash
netstat -anpe | grep "3928" netstat -anpe | grep "39291"
``` ```
</Tabs.Tab> </Tabs.Tab>
</Tabs> </Tabs>

View File

@ -89,11 +89,11 @@
"build:test:darwin": "tsc -p . && electron-builder -p never -m --dir", "build:test:darwin": "tsc -p . && electron-builder -p never -m --dir",
"build:test:win32": "tsc -p . && electron-builder -p never -w --dir", "build:test:win32": "tsc -p . && electron-builder -p never -w --dir",
"build:test:linux": "tsc -p . && electron-builder -p never -l --dir", "build:test:linux": "tsc -p . && electron-builder -p never -l --dir",
"build:darwin": "tsc -p . && electron-builder -p never -m", "build:darwin": "tsc -p . && electron-builder -p never -m --universal",
"build:win32": "tsc -p . && electron-builder -p never -w", "build:win32": "tsc -p . && electron-builder -p never -w",
"build:linux": "tsc -p . && electron-builder -p never -l deb -l AppImage", "build:linux": "tsc -p . && electron-builder -p never -l deb -l AppImage",
"build:publish": "yarn copy:assets && run-script-os", "build:publish": "yarn copy:assets && run-script-os",
"build:publish:darwin": "tsc -p . && electron-builder -p always -m", "build:publish:darwin": "tsc -p . && electron-builder -p always -m --universal",
"build:publish:win32": "tsc -p . && electron-builder -p always -w", "build:publish:win32": "tsc -p . && electron-builder -p always -w",
"build:publish:linux": "tsc -p . && electron-builder -p always -l deb -l AppImage" "build:publish:linux": "tsc -p . && electron-builder -p always -l deb -l AppImage"
}, },

View File

@ -9,9 +9,10 @@
"license": "AGPL-3.0", "license": "AGPL-3.0",
"scripts": { "scripts": {
"clean:modules": "rimraf node_modules/pdf-parse/test && cd node_modules/pdf-parse/lib/pdf.js && rimraf v1.9.426 v1.10.88 v2.0.550", "clean:modules": "rimraf node_modules/pdf-parse/test && cd node_modules/pdf-parse/lib/pdf.js && rimraf v1.9.426 v1.10.88 v2.0.550",
"build-universal-hnswlib": "cd node_modules/hnswlib-node && arch -x86_64 npx node-gyp rebuild --arch=x64 && mv build/Release/addon.node ./addon-amd64.node && node-gyp rebuild --arch=arm64 && mv build/Release/addon.node ./addon-arm64.node && lipo -create -output build/Release/addon.node ./addon-arm64.node ./addon-amd64.node && rm ./addon-arm64.node && rm ./addon-amd64.node",
"build": "yarn clean:modules && tsc --module commonjs && rollup -c rollup.config.ts", "build": "yarn clean:modules && tsc --module commonjs && rollup -c rollup.config.ts",
"build:publish:linux": "rimraf *.tgz --glob && yarn build && npm pack && cpx *.tgz ../../pre-install", "build:publish:linux": "rimraf *.tgz --glob && yarn build && npm pack && cpx *.tgz ../../pre-install",
"build:publish:darwin": "rimraf *.tgz --glob && yarn build && ../../.github/scripts/auto-sign.sh && npm pack && cpx *.tgz ../../pre-install", "build:publish:darwin": "rimraf *.tgz --glob && yarn build-universal-hnswlib && yarn build && ../../.github/scripts/auto-sign.sh && npm pack && cpx *.tgz ../../pre-install",
"build:publish:win32": "rimraf *.tgz --glob && yarn build && npm pack && cpx *.tgz ../../pre-install", "build:publish:win32": "rimraf *.tgz --glob && yarn build && npm pack && cpx *.tgz ../../pre-install",
"build:publish": "run-script-os" "build:publish": "run-script-os"
}, },

View File

@ -1,7 +1,7 @@
{ {
"name": "@janhq/inference-cortex-extension", "name": "@janhq/inference-cortex-extension",
"productName": "Cortex Inference Engine", "productName": "Cortex Inference Engine",
"version": "1.0.23", "version": "1.0.24",
"description": "This extension embeds cortex.cpp, a lightweight inference engine written in C++. See https://jan.ai.\nAdditional dependencies could be installed to run without Cuda Toolkit installation.", "description": "This extension embeds cortex.cpp, a lightweight inference engine written in C++. See https://jan.ai.\nAdditional dependencies could be installed to run without Cuda Toolkit installation.",
"main": "dist/index.js", "main": "dist/index.js",
"node": "dist/node/index.cjs.js", "node": "dist/node/index.cjs.js",

View File

@ -21,7 +21,8 @@
"ctx_len": 4096, "ctx_len": 4096,
"prompt_template": "\n### Instruction:\n{prompt}\n### Response:\n", "prompt_template": "\n### Instruction:\n{prompt}\n### Response:\n",
"llama_model_path": "ggml-model-q5_k.gguf", "llama_model_path": "ggml-model-q5_k.gguf",
"mmproj": "mmproj-model-f16.gguf" "mmproj": "mmproj-model-f16.gguf",
"ngl": 33
}, },
"parameters": { "parameters": {
"max_tokens": 4096 "max_tokens": 4096

View File

@ -21,7 +21,8 @@
"ctx_len": 4096, "ctx_len": 4096,
"prompt_template": "\n### Instruction:\n{prompt}\n### Response:\n", "prompt_template": "\n### Instruction:\n{prompt}\n### Response:\n",
"llama_model_path": "llava-v1.6-vicuna-13b.Q4_K_M.gguf", "llama_model_path": "llava-v1.6-vicuna-13b.Q4_K_M.gguf",
"mmproj": "mmproj-model-f16.gguf" "mmproj": "mmproj-model-f16.gguf",
"ngl": 33
}, },
"parameters": { "parameters": {
"max_tokens": 4096, "max_tokens": 4096,

View File

@ -21,7 +21,8 @@
"ctx_len": 4096, "ctx_len": 4096,
"prompt_template": "\n### Instruction:\n{prompt}\n### Response:\n", "prompt_template": "\n### Instruction:\n{prompt}\n### Response:\n",
"llama_model_path": "llava-v1.6-mistral-7b.Q4_K_M.gguf", "llama_model_path": "llava-v1.6-mistral-7b.Q4_K_M.gguf",
"mmproj": "mmproj-model-f16.gguf" "mmproj": "mmproj-model-f16.gguf",
"ngl": 33
}, },
"parameters": { "parameters": {
"max_tokens": 4096, "max_tokens": 4096,

View File

@ -334,13 +334,22 @@ export default class JanInferenceCortexExtension extends LocalOAIEngine {
} }
}) })
/**
* This is to handle the server segfault issue
*/
this.socket.onclose = (event) => { this.socket.onclose = (event) => {
console.log('WebSocket closed:', event) console.log('WebSocket closed:', event)
// Notify app to update model running state
events.emit(ModelEvent.OnModelStopped, {}) events.emit(ModelEvent.OnModelStopped, {})
// Reconnect to the /events websocket
if (this.shouldReconnect) { if (this.shouldReconnect) {
console.log(`Attempting to reconnect...`) console.log(`Attempting to reconnect...`)
setTimeout(() => this.subscribeToEvents(), 1000) setTimeout(() => this.subscribeToEvents(), 1000)
} }
// Queue up health check
this.queue.add(() => this.healthz())
} }
resolve() resolve()

View File

@ -52,7 +52,10 @@ const ErrorMessage = ({ message }: { message: ThreadMessage }) => {
) )
default: default:
return ( return (
<p data-testid="passthrough-error-message" className="capitalize"> <p
data-testid="passthrough-error-message"
className="first-letter:uppercase"
>
{message.content[0]?.text?.value && ( {message.content[0]?.text?.value && (
<AutoLink text={message.content[0].text.value} /> <AutoLink text={message.content[0].text.value} />
)} )}

View File

@ -187,15 +187,19 @@ const ModelDropdown = ({
], ],
}) })
const overriddenSettings = const defaultContextLength = Math.min(
model?.settings.ctx_len && model.settings.ctx_len > 4096 8192,
? { ctx_len: 4096 } model?.settings.ctx_len ?? 8192
: {} )
const overriddenParameters = {
ctx_len: Math.min(8192, model?.settings.ctx_len ?? 8192),
max_tokens: defaultContextLength,
}
const modelParams = { const modelParams = {
...model?.parameters, ...model?.parameters,
...model?.settings, ...model?.settings,
...overriddenSettings, ...overriddenParameters,
} }
// Update model parameter to the thread state // Update model parameter to the thread state

View File

@ -134,7 +134,7 @@ export default function ModelHandler() {
// Remove non-alphanumeric characters // Remove non-alphanumeric characters
const cleanedMessageContent = messageContent const cleanedMessageContent = messageContent
.replace(/[^a-z0-9\s]/gi, '') .replace(/[^\p{L}\s]+/gu, '')
.trim() .trim()
// Split the message into words // Split the message into words
@ -147,6 +147,9 @@ export default function ModelHandler() {
return return
} }
// Do not persist empty message
if (!cleanedMessageContent.trim().length) return
const updatedThread: Thread = { const updatedThread: Thread = {
...thread, ...thread,

View File

@ -105,14 +105,21 @@ export const useCreateNewThread = () => {
enabled: true, enabled: true,
settings: assistant.tools && assistant.tools[0].settings, settings: assistant.tools && assistant.tools[0].settings,
} }
const overriddenSettings =
defaultModel?.settings.ctx_len && defaultModel.settings.ctx_len > 2048
? { ctx_len: 4096 }
: {}
const overriddenParameters = defaultModel?.parameters.max_tokens // Default context length is 8192
? { max_tokens: 4096 } const defaultContextLength = Math.min(
: {} 8192,
defaultModel?.settings.ctx_len ?? 8192
)
const overriddenSettings = {
ctx_len: defaultContextLength,
}
// Use ctx length by default
const overriddenParameters = {
max_tokens: defaultContextLength,
}
const createdAt = Date.now() const createdAt = Date.now()
let instructions: string | undefined = assistant.instructions let instructions: string | undefined = assistant.instructions

View File

@ -10,7 +10,6 @@ import {
ConversationalExtension, ConversationalExtension,
EngineManager, EngineManager,
ToolManager, ToolManager,
ChatCompletionMessage,
} from '@janhq/core' } from '@janhq/core'
import { extractInferenceParams, extractModelLoadParams } from '@janhq/core' import { extractInferenceParams, extractModelLoadParams } from '@janhq/core'
import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai' import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai'
@ -21,7 +20,6 @@ import {
fileUploadAtom, fileUploadAtom,
} from '@/containers/Providers/Jotai' } from '@/containers/Providers/Jotai'
import { Stack } from '@/utils/Stack'
import { compressImage, getBase64 } from '@/utils/base64' import { compressImage, getBase64 } from '@/utils/base64'
import { MessageRequestBuilder } from '@/utils/messageRequestBuilder' import { MessageRequestBuilder } from '@/utils/messageRequestBuilder'
@ -86,33 +84,6 @@ export default function useSendChatMessage() {
selectedModelRef.current = selectedModel selectedModelRef.current = selectedModel
}, [selectedModel]) }, [selectedModel])
const normalizeMessages = (
messages: ChatCompletionMessage[]
): ChatCompletionMessage[] => {
const stack = new Stack<ChatCompletionMessage>()
for (const message of messages) {
if (stack.isEmpty()) {
stack.push(message)
continue
}
const topMessage = stack.peek()
if (message.role === topMessage.role) {
// add an empty message
stack.push({
role:
topMessage.role === ChatCompletionRole.User
? ChatCompletionRole.Assistant
: ChatCompletionRole.User,
content: '.', // some model requires not empty message
})
}
stack.push(message)
}
return stack.reverseOutput()
}
const resendChatMessage = async (currentMessage: ThreadMessage) => { const resendChatMessage = async (currentMessage: ThreadMessage) => {
// Delete last response before regenerating // Delete last response before regenerating
const newConvoData = currentMessages const newConvoData = currentMessages
@ -247,7 +218,6 @@ export default function useSendChatMessage() {
(assistant) => assistant.tools ?? [] (assistant) => assistant.tools ?? []
) ?? [] ) ?? []
) )
request.messages = normalizeMessages(request.messages ?? [])
// Request for inference // Request for inference
EngineManager.instance() EngineManager.instance()

View File

@ -38,7 +38,7 @@
"react-icons": "^4.12.0", "react-icons": "^4.12.0",
"react-markdown": "^9.0.1", "react-markdown": "^9.0.1",
"react-toastify": "^9.1.3", "react-toastify": "^9.1.3",
"rehype-highlight": "^6.0.0", "rehype-highlight": "^7.0.1",
"rehype-highlight-code-lines": "^1.0.4", "rehype-highlight-code-lines": "^1.0.4",
"rehype-katex": "^7.0.1", "rehype-katex": "^7.0.1",
"rehype-raw": "^7.0.0", "rehype-raw": "^7.0.0",

View File

@ -14,9 +14,11 @@ import LoadModelError from '../LoadModelError'
import EmptyThread from './EmptyThread' import EmptyThread from './EmptyThread'
import { getCurrentChatMessagesAtom } from '@/helpers/atoms/ChatMessage.atom' import { getCurrentChatMessagesAtom } from '@/helpers/atoms/ChatMessage.atom'
import { activeThreadAtom } from '@/helpers/atoms/Thread.atom'
const ChatConfigurator = memo(() => { const ChatConfigurator = memo(() => {
const messages = useAtomValue(getCurrentChatMessagesAtom) const messages = useAtomValue(getCurrentChatMessagesAtom)
const currentThread = useAtomValue(activeThreadAtom)
const [current, setCurrent] = useState<ThreadMessage[]>([]) const [current, setCurrent] = useState<ThreadMessage[]>([])
const loadModelError = useAtomValue(loadModelErrorAtom) const loadModelError = useAtomValue(loadModelErrorAtom)
@ -31,12 +33,12 @@ const ChatConfigurator = memo(() => {
useEffect(() => { useEffect(() => {
if ( if (
messages?.length !== current?.length || !isMessagesIdentificial(messages, current) ||
!isMessagesIdentificial(messages, current) messages.some((e) => e.thread_id !== currentThread?.id)
) { ) {
setCurrent(messages) setCurrent(messages)
} }
}, [messages, current, loadModelError]) }, [messages, current, loadModelError, currentThread])
if (!messages.length) return <EmptyThread /> if (!messages.length) return <EmptyThread />
return ( return (
@ -119,7 +121,7 @@ const ChatBody = memo(
> >
{items.map((virtualRow) => ( {items.map((virtualRow) => (
<div <div
key={virtualRow.key} key={messages[virtualRow.index]?.id}
data-index={virtualRow.index} data-index={virtualRow.index}
ref={virtualizer.measureElement} ref={virtualizer.measureElement}
> >

View File

@ -54,7 +54,12 @@ const ChatItem = forwardRef<Ref, Props>((message, ref) => {
<> <>
{status !== MessageStatus.Error && content?.length > 0 && ( {status !== MessageStatus.Error && content?.length > 0 && (
<div ref={ref} className="relative"> <div ref={ref} className="relative">
<MessageContainer {...message} content={content} status={status} /> <MessageContainer
{...message}
content={content}
status={status}
isCurrentMessage={message.isCurrentMessage ?? false}
/>
</div> </div>
)} )}
{errorMessage && !message.loadModelError && ( {errorMessage && !message.loadModelError && (

View File

@ -49,7 +49,9 @@ const LoadModelError = () => {
} else { } else {
return ( return (
<div className="mx-6 flex flex-col items-center space-y-2 text-center font-medium text-[hsla(var(--text-secondary))]"> <div className="mx-6 flex flex-col items-center space-y-2 text-center font-medium text-[hsla(var(--text-secondary))]">
{loadModelError && <p className="capitalize">{loadModelError}</p>} {loadModelError && (
<p className="first-letter:uppercase">{loadModelError}</p>
)}
<p> <p>
{`Something's wrong.`}&nbsp;Access&nbsp; {`Something's wrong.`}&nbsp;Access&nbsp;
<span <span

View File

@ -19,19 +19,18 @@ import { MarkdownTextMessage } from './MarkdownTextMessage'
import { import {
editMessageAtom, editMessageAtom,
getCurrentChatMessagesAtom,
tokenSpeedAtom, tokenSpeedAtom,
} from '@/helpers/atoms/ChatMessage.atom' } from '@/helpers/atoms/ChatMessage.atom'
import { activeThreadAtom } from '@/helpers/atoms/Thread.atom' import { activeThreadAtom } from '@/helpers/atoms/Thread.atom'
const MessageContainer: React.FC<ThreadMessage> = (props) => { const MessageContainer: React.FC<
ThreadMessage & { isCurrentMessage: boolean }
> = (props) => {
const isUser = props.role === ChatCompletionRole.User const isUser = props.role === ChatCompletionRole.User
const isSystem = props.role === ChatCompletionRole.System const isSystem = props.role === ChatCompletionRole.System
const editMessage = useAtomValue(editMessageAtom) const editMessage = useAtomValue(editMessageAtom)
const activeThread = useAtomValue(activeThreadAtom) const activeThread = useAtomValue(activeThreadAtom)
const tokenSpeed = useAtomValue(tokenSpeedAtom) const tokenSpeed = useAtomValue(tokenSpeedAtom)
const messages = useAtomValue(getCurrentChatMessagesAtom)
const text = useMemo( const text = useMemo(
() => props.content[0]?.text?.value ?? '', () => props.content[0]?.text?.value ?? '',
@ -81,16 +80,6 @@ const MessageContainer: React.FC<ThreadMessage> = (props) => {
<p className="text-xs font-medium text-gray-400"> <p className="text-xs font-medium text-gray-400">
{displayDate(props.created)} {displayDate(props.created)}
</p> </p>
<div
className={twMerge(
'absolute right-0 cursor-pointer transition-all',
messages[messages.length - 1]?.id === props.id && !isUser
? 'absolute -bottom-8 right-4'
: 'hidden group-hover:absolute group-hover:right-4 group-hover:top-4 group-hover:flex'
)}
>
<MessageToolbar message={props} />
</div>
{tokenSpeed && {tokenSpeed &&
tokenSpeed.message === props.id && tokenSpeed.message === props.id &&
tokenSpeed.tokenSpeed > 0 && ( tokenSpeed.tokenSpeed > 0 && (
@ -100,39 +89,52 @@ const MessageContainer: React.FC<ThreadMessage> = (props) => {
)} )}
</div> </div>
<div <div className="flex w-full flex-col">
className={twMerge( <div
'w-full', className={twMerge(
!isUser && !text.includes(' ') && 'break-all' 'absolute right-0 order-1 mt-2 flex cursor-pointer items-center justify-start gap-x-2 transition-all',
)} props.isCurrentMessage && !isUser
> ? 'relative order-2 flex justify-end'
<> : 'hidden group-hover:absolute group-hover:right-4 group-hover:top-4 group-hover:flex'
{messageType === ContentType.Image && (
<ImageMessage content={props.content[0]} />
)} )}
{messageType === ContentType.Pdf && ( >
<DocMessage <MessageToolbar message={props} />
id={props.id} </div>
name={props.content[0]?.text?.name} <div
size={props.content[0]?.text?.size} className={twMerge(
/> 'order-2 w-full',
!isUser && !text.includes(' ') && 'break-all',
props.isCurrentMessage && !isUser && 'order-1'
)} )}
>
<>
{messageType === ContentType.Image && (
<ImageMessage content={props.content[0]} />
)}
{messageType === ContentType.Pdf && (
<DocMessage
id={props.id}
name={props.content[0]?.text?.name}
size={props.content[0]?.text?.size}
/>
)}
{editMessage === props.id ? ( {editMessage === props.id ? (
<div> <div>
<EditChatInput message={props} /> <EditChatInput message={props} />
</div> </div>
) : ( ) : (
<div <div
className={twMerge( className={twMerge(
'message max-width-[100%] flex flex-col gap-y-2 overflow-x-auto overflow-y-hidden leading-relaxed' 'message max-width-[100%] flex flex-col gap-y-2 overflow-x-auto overflow-y-hidden leading-relaxed'
)} )}
dir="ltr" dir="ltr"
> >
<MarkdownTextMessage id={props.id} text={text} /> <MarkdownTextMessage id={props.id} text={text} />
</div> </div>
)} )}
</> </>
</div>
</div> </div>
</div> </div>
) )

View File

@ -15,6 +15,8 @@ import { ulid } from 'ulidx'
import { FileType } from '@/containers/Providers/Jotai' import { FileType } from '@/containers/Providers/Jotai'
import { Stack } from '@/utils/Stack'
export class MessageRequestBuilder { export class MessageRequestBuilder {
msgId: string msgId: string
type: MessageRequestType type: MessageRequestType
@ -36,7 +38,7 @@ export class MessageRequestBuilder {
.filter((e) => e.status !== MessageStatus.Error) .filter((e) => e.status !== MessageStatus.Error)
.map<ChatCompletionMessage>((msg) => ({ .map<ChatCompletionMessage>((msg) => ({
role: msg.role, role: msg.role,
content: msg.content[0]?.text.value ?? '', content: msg.content[0]?.text.value ?? '.',
})) }))
} }
@ -130,12 +132,39 @@ export class MessageRequestBuilder {
return this return this
} }
normalizeMessages = (
messages: ChatCompletionMessage[]
): ChatCompletionMessage[] => {
const stack = new Stack<ChatCompletionMessage>()
for (const message of messages) {
if (stack.isEmpty()) {
stack.push(message)
continue
}
const topMessage = stack.peek()
if (message.role === topMessage.role) {
// add an empty message
stack.push({
role:
topMessage.role === ChatCompletionRole.User
? ChatCompletionRole.Assistant
: ChatCompletionRole.User,
content: '.', // some model requires not empty message
})
}
stack.push(message)
}
return stack.reverseOutput()
}
build(): MessageRequest { build(): MessageRequest {
return { return {
id: this.msgId, id: this.msgId,
type: this.type, type: this.type,
threadId: this.thread.id, threadId: this.thread.id,
messages: this.messages, messages: this.normalizeMessages(this.messages),
model: this.model, model: this.model,
thread: this.thread, thread: this.thread,
} }