mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-11-19 20:50:09 +01:00
Merge branch 'master' of github.com:Mintplex-Labs/anything-llm into render
This commit is contained in:
commit
08825ba30e
77
.github/workflows/dev-build.yaml
vendored
Normal file
77
.github/workflows/dev-build.yaml
vendored
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
name: Publish AnythingLLM Development Docker image (amd64)
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: build-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ['1915-docker-perms'] # master branch only. Do not modify.
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
- 'cloud-deployments/*'
|
||||||
|
- 'images/**/*'
|
||||||
|
- '.vscode/**/*'
|
||||||
|
- '**/.env.example'
|
||||||
|
- '.github/ISSUE_TEMPLATE/**/*'
|
||||||
|
- 'embed/**/*' # Embed should be published to frontend (yarn build:publish) if any changes are introduced
|
||||||
|
- 'server/utils/agents/aibitat/example/**/*' # Do not push new image for local dev testing of new aibitat images.
|
||||||
|
- 'docker/vex/*' # CVE exceptions we know are not in risk
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
push_multi_platform_to_registries:
|
||||||
|
name: Push Docker multi-platform image to multiple registries
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Check out the repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Check if DockerHub build needed
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
# Check if the secret for USERNAME is set (don't even check for the password)
|
||||||
|
if [[ -z "${{ secrets.DOCKER_USERNAME }}" ]]; then
|
||||||
|
echo "DockerHub build not needed"
|
||||||
|
echo "enabled=false" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "DockerHub build needed"
|
||||||
|
echo "enabled=true" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
id: dockerhub
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
|
||||||
|
# Only login to the Docker Hub if the repo is mintplex/anythingllm, to allow for forks to build on GHCR
|
||||||
|
if: steps.dockerhub.outputs.enabled == 'true'
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Extract metadata (tags, labels) for Docker
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
${{ steps.dockerhub.outputs.enabled == 'true' && 'mintplexlabs/anythingllm' || '' }}
|
||||||
|
tags: |
|
||||||
|
type=raw,value=dev
|
||||||
|
|
||||||
|
- name: Build and push multi-platform Docker image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./docker/Dockerfile
|
||||||
|
push: true
|
||||||
|
sbom: true
|
||||||
|
provenance: mode=max
|
||||||
|
platforms: linux/amd64
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -11,6 +11,7 @@
|
|||||||
"comkey",
|
"comkey",
|
||||||
"cooldown",
|
"cooldown",
|
||||||
"cooldowns",
|
"cooldowns",
|
||||||
|
"datafile",
|
||||||
"Deduplicator",
|
"Deduplicator",
|
||||||
"Dockerized",
|
"Dockerized",
|
||||||
"docpath",
|
"docpath",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<a name="readme-top"></a>
|
<a name="readme-top"></a>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://useanything.com"><img src="https://github.com/Mintplex-Labs/anything-llm/blob/master/images/wordmark.png?raw=true" alt="AnythingLLM logo"></a>
|
<a href="https://anythingllm.com"><img src="https://github.com/Mintplex-Labs/anything-llm/blob/master/images/wordmark.png?raw=true" alt="AnythingLLM logo"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div align='center'>
|
<div align='center'>
|
||||||
@ -20,7 +20,7 @@
|
|||||||
<a href="https://github.com/Mintplex-Labs/anything-llm/blob/master/LICENSE" target="_blank">
|
<a href="https://github.com/Mintplex-Labs/anything-llm/blob/master/LICENSE" target="_blank">
|
||||||
<img src="https://img.shields.io/static/v1?label=license&message=MIT&color=white" alt="License">
|
<img src="https://img.shields.io/static/v1?label=license&message=MIT&color=white" alt="License">
|
||||||
</a> |
|
</a> |
|
||||||
<a href="https://docs.useanything.com" target="_blank">
|
<a href="https://docs.anythingllm.com" target="_blank">
|
||||||
Docs
|
Docs
|
||||||
</a> |
|
</a> |
|
||||||
<a href="https://my.mintplexlabs.com/aio-checkout?product=anythingllm" target="_blank">
|
<a href="https://my.mintplexlabs.com/aio-checkout?product=anythingllm" target="_blank">
|
||||||
@ -33,7 +33,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
👉 AnythingLLM for desktop (Mac, Windows, & Linux)! <a href="https://useanything.com/download" target="_blank"> Download Now</a>
|
👉 AnythingLLM for desktop (Mac, Windows, & Linux)! <a href="https://anythingllm.com/download" target="_blank"> Download Now</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
A full-stack application that enables you to turn any document, resource, or piece of content into context that any LLM can use as references during chatting. This application allows you to pick and choose which LLM or Vector Database you want to use as well as supporting multi-user management and permissions.
|
A full-stack application that enables you to turn any document, resource, or piece of content into context that any LLM can use as references during chatting. This application allows you to pick and choose which LLM or Vector Database you want to use as well as supporting multi-user management and permissions.
|
||||||
|
@ -60,7 +60,7 @@ ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
|||||||
ENV CHROME_PATH=/app/chrome-linux/chrome
|
ENV CHROME_PATH=/app/chrome-linux/chrome
|
||||||
ENV PUPPETEER_EXECUTABLE_PATH=/app/chrome-linux/chrome
|
ENV PUPPETEER_EXECUTABLE_PATH=/app/chrome-linux/chrome
|
||||||
|
|
||||||
RUN echo "Done running arm64 specific installtion steps"
|
RUN echo "Done running arm64 specific installation steps"
|
||||||
|
|
||||||
#############################################
|
#############################################
|
||||||
|
|
||||||
@ -129,12 +129,19 @@ RUN yarn build && \
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Install server layer & build node-llama-cpp
|
# Install server layer & build node-llama-cpp
|
||||||
FROM build AS server-build
|
# Also pull and build collector deps (chromium issues prevent bad bindings)
|
||||||
|
FROM build AS backend-build
|
||||||
COPY ./server /app/server/
|
COPY ./server /app/server/
|
||||||
WORKDIR /app/server
|
WORKDIR /app/server
|
||||||
RUN yarn install --production --network-timeout 100000 && yarn cache clean
|
RUN yarn install --production --network-timeout 100000 && yarn cache clean
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install collector dependencies
|
||||||
|
COPY ./collector/ ./collector/
|
||||||
|
WORKDIR /app/collector
|
||||||
|
ENV PUPPETEER_DOWNLOAD_BASE_URL=https://storage.googleapis.com/chrome-for-testing-public
|
||||||
|
RUN yarn install --production --network-timeout 100000 && yarn cache clean
|
||||||
|
|
||||||
# Compile Llama.cpp bindings for node-llama-cpp for this operating system.
|
# Compile Llama.cpp bindings for node-llama-cpp for this operating system.
|
||||||
# Creates appropriate bindings for the OS
|
# Creates appropriate bindings for the OS
|
||||||
USER root
|
USER root
|
||||||
@ -143,24 +150,14 @@ RUN npx --no node-llama-cpp download
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
USER anythingllm
|
USER anythingllm
|
||||||
|
|
||||||
# Build collector deps (this also downloads proper chrome for collector in /app/.cache so that needs to be
|
# Since we are building from backend-build we just need to move built frontend into server/public
|
||||||
# transferred properly in prod-build stage.
|
FROM backend-build AS production-build
|
||||||
FROM build AS collector-build
|
|
||||||
COPY ./collector /app/collector
|
|
||||||
WORKDIR /app/collector
|
|
||||||
ENV PUPPETEER_DOWNLOAD_BASE_URL=https://storage.googleapis.com/chrome-for-testing-public
|
|
||||||
RUN yarn install --production --network-timeout 100000 && yarn cache clean
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
FROM build AS production-build
|
|
||||||
WORKDIR /app
|
|
||||||
# Copy the server
|
|
||||||
COPY --chown=anythingllm:anythingllm --from=server-build /app/server/ /app/server/
|
|
||||||
# Copy built static frontend files to the server public directory
|
|
||||||
COPY --chown=anythingllm:anythingllm --from=frontend-build /app/frontend/dist /app/server/public
|
COPY --chown=anythingllm:anythingllm --from=frontend-build /app/frontend/dist /app/server/public
|
||||||
# Copy the collector
|
USER root
|
||||||
COPY --chown=anythingllm:anythingllm --from=collector-build /app/collector/ /app/collector/
|
RUN chown -R anythingllm:anythingllm /app/server && \
|
||||||
COPY --chown=anythingllm:anythingllm --from=collector-build /app/.cache/puppeteer /app/.cache/puppeteer
|
chown -R anythingllm:anythingllm /app/collector
|
||||||
|
USER anythingllm
|
||||||
|
|
||||||
# No longer needed? (deprecated)
|
# No longer needed? (deprecated)
|
||||||
# WORKDIR /app/server
|
# WORKDIR /app/server
|
||||||
|
51
docker/vex/CVE-2019-10790.vex.json
Normal file
51
docker/vex/CVE-2019-10790.vex.json
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"@context": "https://openvex.dev/ns/v0.2.0",
|
||||||
|
"@id": "https://openvex.dev/docs/public/vex-6750d79bb005487e11d10f81d0b3ac92c47e6e259292c6b2d02558f9f4bca52d",
|
||||||
|
"author": "tim@mintplexlabs.com",
|
||||||
|
"timestamp": "2024-07-22T13:49:12.883675-07:00",
|
||||||
|
"version": 1,
|
||||||
|
"statements": [
|
||||||
|
{
|
||||||
|
"vulnerability": {
|
||||||
|
"name": "CVE-2019-10790"
|
||||||
|
},
|
||||||
|
"timestamp": "2024-07-22T13:49:12.883678-07:00",
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"@id": "pkg:docker/mintplexlabs/anythingllm@render",
|
||||||
|
"subcomponents": [
|
||||||
|
{
|
||||||
|
"@id": "pkg:npm/taffydb@2.6.2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@id": "pkg:docker/mintplexlabs/anythingllm@railway",
|
||||||
|
"subcomponents": [
|
||||||
|
{
|
||||||
|
"@id": "pkg:npm/taffydb@2.6.2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@id": "pkg:docker/mintplexlabs/anythingllm@latest",
|
||||||
|
"subcomponents": [
|
||||||
|
{
|
||||||
|
"@id": "pkg:npm/taffydb@2.6.2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@id": "pkg:docker/mintplexlabs/anythingllm@master",
|
||||||
|
"subcomponents": [
|
||||||
|
{
|
||||||
|
"@id": "pkg:npm/taffydb@2.6.2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"status": "not_affected",
|
||||||
|
"justification": "vulnerable_code_cannot_be_controlled_by_adversary"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -19,7 +19,7 @@ const DEFAULT_SETTINGS = {
|
|||||||
assistantBgColor: "#2563eb", // assistant text bubble color
|
assistantBgColor: "#2563eb", // assistant text bubble color
|
||||||
noSponsor: null, // Shows sponsor in footer of chat
|
noSponsor: null, // Shows sponsor in footer of chat
|
||||||
sponsorText: "Powered by AnythingLLM", // default sponsor text
|
sponsorText: "Powered by AnythingLLM", // default sponsor text
|
||||||
sponsorLink: "https://useanything.com", // default sponsor link
|
sponsorLink: "https://anythingllm.com", // default sponsor link
|
||||||
position: "bottom-right", // position of chat button/window
|
position: "bottom-right", // position of chat button/window
|
||||||
assistantName: "AnythingLLM Chat Assistant", // default assistant name
|
assistantName: "AnythingLLM Chat Assistant", // default assistant name
|
||||||
assistantIcon: null, // default assistant icon
|
assistantIcon: null, // default assistant icon
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
<!-- Facebook -->
|
<!-- Facebook -->
|
||||||
<meta property="og:type" content="website">
|
<meta property="og:type" content="website">
|
||||||
<meta property="og:url" content="https://useanything.com">
|
<meta property="og:url" content="https://anythingllm.com">
|
||||||
<meta property="og:title" content="AnythingLLM | Your personal LLM trained on anything">
|
<meta property="og:title" content="AnythingLLM | Your personal LLM trained on anything">
|
||||||
<meta property="og:description" content="AnythingLLM | Your personal LLM trained on anything">
|
<meta property="og:description" content="AnythingLLM | Your personal LLM trained on anything">
|
||||||
<meta property="og:image"
|
<meta property="og:image"
|
||||||
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
<!-- Twitter -->
|
<!-- Twitter -->
|
||||||
<meta property="twitter:card" content="summary_large_image">
|
<meta property="twitter:card" content="summary_large_image">
|
||||||
<meta property="twitter:url" content="https://useanything.com">
|
<meta property="twitter:url" content="https://anythingllm.com">
|
||||||
<meta property="twitter:title" content="AnythingLLM | Your personal LLM trained on anything">
|
<meta property="twitter:title" content="AnythingLLM | Your personal LLM trained on anything">
|
||||||
<meta property="twitter:description" content="AnythingLLM | Your personal LLM trained on anything">
|
<meta property="twitter:description" content="AnythingLLM | Your personal LLM trained on anything">
|
||||||
<meta property="twitter:image"
|
<meta property="twitter:image"
|
||||||
|
File diff suppressed because one or more lines are too long
@ -63,6 +63,7 @@ const ExperimentalFeatures = lazy(
|
|||||||
const LiveDocumentSyncManage = lazy(
|
const LiveDocumentSyncManage = lazy(
|
||||||
() => import("@/pages/Admin/ExperimentalFeatures/Features/LiveSync/manage")
|
() => import("@/pages/Admin/ExperimentalFeatures/Features/LiveSync/manage")
|
||||||
);
|
);
|
||||||
|
const FineTuningWalkthrough = lazy(() => import("@/pages/FineTuning"));
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
@ -186,6 +187,11 @@ export default function App() {
|
|||||||
path="/settings/beta-features/live-document-sync/manage"
|
path="/settings/beta-features/live-document-sync/manage"
|
||||||
element={<AdminRoute Component={LiveDocumentSyncManage} />}
|
element={<AdminRoute Component={LiveDocumentSyncManage} />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/fine-tuning"
|
||||||
|
element={<AdminRoute Component={FineTuningWalkthrough} />}
|
||||||
|
/>
|
||||||
</Routes>
|
</Routes>
|
||||||
<ToastContainer />
|
<ToastContainer />
|
||||||
</I18nextProvider>
|
</I18nextProvider>
|
||||||
|
@ -111,6 +111,35 @@ export default function OllamaLLMOptions({ settings }) {
|
|||||||
Enter the URL where Ollama is running.
|
Enter the URL where Ollama is running.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col w-60">
|
||||||
|
<label className="text-white text-sm font-semibold block mb-2">
|
||||||
|
Ollama Keep Alive
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
name="OllamaLLMKeepAliveSeconds"
|
||||||
|
required={true}
|
||||||
|
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
|
||||||
|
defaultValue={settings?.OllamaLLMKeepAliveSeconds ?? "300"}
|
||||||
|
>
|
||||||
|
<option value="0">No cache</option>
|
||||||
|
<option value="300">5 minutes</option>
|
||||||
|
<option value="3600">1 hour</option>
|
||||||
|
<option value="-1">Forever</option>
|
||||||
|
</select>
|
||||||
|
<p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2">
|
||||||
|
Choose how long Ollama should keep your model in memory before
|
||||||
|
unloading.
|
||||||
|
<a
|
||||||
|
className="underline text-blue-300"
|
||||||
|
href="https://github.com/ollama/ollama/blob/main/docs/faq.md#how-do-i-keep-a-model-loaded-in-memory-or-make-it-unload-immediately"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
|
Learn more →
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,6 +21,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import showToast from "@/utils/toast";
|
import showToast from "@/utils/toast";
|
||||||
import System from "@/models/system";
|
import System from "@/models/system";
|
||||||
import Option from "./MenuOption";
|
import Option from "./MenuOption";
|
||||||
|
import { FineTuningAlert } from "@/pages/FineTuning/Banner";
|
||||||
|
|
||||||
export default function SettingsSidebar() {
|
export default function SettingsSidebar() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -132,48 +133,53 @@ export default function SettingsSidebar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
<Link
|
<div>
|
||||||
to={paths.home()}
|
<Link
|
||||||
className="flex shrink-0 max-w-[55%] items-center justify-start mx-[38px] my-[18px]"
|
to={paths.home()}
|
||||||
>
|
className="flex shrink-0 max-w-[55%] items-center justify-start mx-[38px] my-[18px]"
|
||||||
<img
|
>
|
||||||
src={logo}
|
<img
|
||||||
alt="Logo"
|
src={logo}
|
||||||
className="rounded max-h-[24px]"
|
alt="Logo"
|
||||||
style={{ objectFit: "contain" }}
|
className="rounded max-h-[24px]"
|
||||||
/>
|
style={{ objectFit: "contain" }}
|
||||||
</Link>
|
/>
|
||||||
<div
|
</Link>
|
||||||
ref={sidebarRef}
|
<div
|
||||||
className="transition-all duration-500 relative m-[16px] rounded-[16px] bg-sidebar border-2 border-outline min-w-[250px] p-[10px] h-[calc(100%-76px)]"
|
ref={sidebarRef}
|
||||||
>
|
className="transition-all duration-500 relative m-[16px] rounded-[16px] bg-sidebar border-2 border-outline min-w-[250px] p-[10px] h-[calc(100%-76px)]"
|
||||||
<div className="w-full h-full flex flex-col overflow-x-hidden items-between min-w-[235px]">
|
>
|
||||||
<div className="text-white text-opacity-60 text-sm font-medium uppercase mt-[4px] mb-0 ml-2">
|
<div className="w-full h-full flex flex-col overflow-x-hidden items-between min-w-[235px]">
|
||||||
{t("settings.title")}
|
<div className="text-white text-opacity-60 text-sm font-medium uppercase mt-[4px] mb-0 ml-2">
|
||||||
</div>
|
{t("settings.title")}
|
||||||
<div className="relative h-[calc(100%-60px)] flex flex-col w-full justify-between pt-[10px] overflow-y-scroll no-scroll">
|
</div>
|
||||||
<div className="h-auto sidebar-items">
|
<div className="relative h-[calc(100%-60px)] flex flex-col w-full justify-between pt-[10px] overflow-y-scroll no-scroll">
|
||||||
<div className="flex flex-col gap-y-2 pb-[60px] overflow-y-scroll no-scroll">
|
<div className="h-auto sidebar-items">
|
||||||
<SidebarOptions user={user} t={t} />
|
<div className="flex flex-col gap-y-2 pb-[60px] overflow-y-scroll no-scroll">
|
||||||
<div className="h-[1.5px] bg-[#3D4147] mx-3 mt-[14px]" />
|
<SidebarOptions user={user} t={t} />
|
||||||
<SupportEmail />
|
<div className="h-[1.5px] bg-[#3D4147] mx-3 mt-[14px]" />
|
||||||
<Link
|
<SupportEmail />
|
||||||
hidden={user?.hasOwnProperty("role") && user.role !== "admin"}
|
<Link
|
||||||
to={paths.settings.privacy()}
|
hidden={
|
||||||
className="text-darker hover:text-white text-xs leading-[18px] mx-3"
|
user?.hasOwnProperty("role") && user.role !== "admin"
|
||||||
>
|
}
|
||||||
{t("settings.privacy")}
|
to={paths.settings.privacy()}
|
||||||
</Link>
|
className="text-darker hover:text-white text-xs leading-[18px] mx-3"
|
||||||
|
>
|
||||||
|
{t("settings.privacy")}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="absolute bottom-0 left-0 right-0 pt-4 pb-3 rounded-b-[16px] bg-sidebar bg-opacity-80 backdrop-filter backdrop-blur-md z-10">
|
||||||
<div className="absolute bottom-0 left-0 right-0 pt-4 pb-3 rounded-b-[16px] bg-sidebar bg-opacity-80 backdrop-filter backdrop-blur-md z-10">
|
<Footer />
|
||||||
<Footer />
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<FineTuningAlert />
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -766,3 +766,31 @@ does not extend the close button beyond the viewport. */
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.top-banner {
|
||||||
|
animation: popTop 500ms forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes popTop {
|
||||||
|
0% {
|
||||||
|
top: -3.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
top: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rm-top-banner {
|
||||||
|
animation: rmPopTop 500ms forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rmPopTop {
|
||||||
|
0% {
|
||||||
|
top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
top: -3.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
129
frontend/src/models/experimental/fineTuning.js
Normal file
129
frontend/src/models/experimental/fineTuning.js
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
import { API_BASE } from "@/utils/constants";
|
||||||
|
import { baseHeaders, safeJsonParse } from "@/utils/request";
|
||||||
|
|
||||||
|
const FineTuning = {
|
||||||
|
cacheKeys: {
|
||||||
|
dismissed_cta: "anythingllm_dismissed_fine_tune_notif",
|
||||||
|
eligibility: "anythingllm_can_fine_tune",
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the information for the Fine-tuning product to display in various frontends
|
||||||
|
* @returns {Promise<{
|
||||||
|
* productDetails: {
|
||||||
|
* name: string,
|
||||||
|
* description: string,
|
||||||
|
* icon: string,
|
||||||
|
* active: boolean,
|
||||||
|
* },
|
||||||
|
* pricing: {
|
||||||
|
* usd: number,
|
||||||
|
* },
|
||||||
|
* availableBaseModels: string[]
|
||||||
|
* }>}
|
||||||
|
*/
|
||||||
|
info: async function () {
|
||||||
|
return await fetch(`${API_BASE}/experimental/fine-tuning/info`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: baseHeaders(),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (!res.ok) throw new Error("Could not get model info.");
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.then((res) => res)
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
datasetStat: async function ({ slugs = [], feedback = null }) {
|
||||||
|
return await fetch(`${API_BASE}/experimental/fine-tuning/dataset`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: baseHeaders(),
|
||||||
|
body: JSON.stringify({ slugs, feedback }),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (!res.ok) throw new Error("Could not get dataset info.");
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.then((res) => res)
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
return { count: null };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Generates Fine-Tuning order.
|
||||||
|
* @param {{email:string, baseModel:string, modelName: string, trainingData: {slugs:string[], feedback:boolean|null}}} param0
|
||||||
|
* @returns {Promise<{checkoutUrl:string, jobId:string}|null>}
|
||||||
|
*/
|
||||||
|
createOrder: async function ({ email, baseModel, modelName, trainingData }) {
|
||||||
|
return await fetch(`${API_BASE}/experimental/fine-tuning/order`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: baseHeaders(),
|
||||||
|
body: JSON.stringify({
|
||||||
|
email,
|
||||||
|
baseModel,
|
||||||
|
modelName,
|
||||||
|
trainingData,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (!res.ok) throw new Error("Could not order fine-tune.");
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.then((res) => res)
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if a user should see the CTA alert. In general this alert
|
||||||
|
* Can only render if the user is empty (single user) or is an admin role.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
canAlert: function (user = null) {
|
||||||
|
if (!!user && user.role !== "admin") return false;
|
||||||
|
return !window?.localStorage?.getItem(this.cacheKeys.dismissed_cta);
|
||||||
|
},
|
||||||
|
checkEligibility: async function () {
|
||||||
|
const cache = window.localStorage.getItem(this.cacheKeys.eligibility);
|
||||||
|
if (!!cache) {
|
||||||
|
const { data, lastFetched } = safeJsonParse(cache, {
|
||||||
|
data: null,
|
||||||
|
lastFetched: 0,
|
||||||
|
});
|
||||||
|
if (!!data && Date.now() - lastFetched < 1.8e7)
|
||||||
|
// 5 hours
|
||||||
|
return data.eligible;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await fetch(`${API_BASE}/experimental/fine-tuning/check-eligible`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: baseHeaders(),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (!res.ok) throw new Error("Could not check if eligible.");
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
window.localStorage.setItem(
|
||||||
|
this.cacheKeys.eligibility,
|
||||||
|
JSON.stringify({
|
||||||
|
data: { eligible: res.eligible },
|
||||||
|
lastFetched: Date.now(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return res.eligible;
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FineTuning;
|
@ -66,7 +66,7 @@ export default function LiveSyncToggle({ enabled = false, onToggle }) {
|
|||||||
<ul className="space-y-2">
|
<ul className="space-y-2">
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href="https://docs.useanything.com/beta-preview/active-features/live-document-sync"
|
href="https://docs.anythingllm.com/beta-preview/active-features/live-document-sync"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="text-sm text-blue-400 hover:underline flex items-center gap-x-1"
|
className="text-sm text-blue-400 hover:underline flex items-center gap-x-1"
|
||||||
>
|
>
|
||||||
|
@ -240,10 +240,10 @@ function FeatureVerification({ children }) {
|
|||||||
Access to any features requires approval of this modal. If
|
Access to any features requires approval of this modal. If
|
||||||
you would like to read more you can refer to{" "}
|
you would like to read more you can refer to{" "}
|
||||||
<a
|
<a
|
||||||
href="https://docs.useanything.com/beta-preview/overview"
|
href="https://docs.anythingllm.com/beta-preview/overview"
|
||||||
className="underline text-blue-500"
|
className="underline text-blue-500"
|
||||||
>
|
>
|
||||||
docs.useanything.com
|
docs.anythingllm.com
|
||||||
</a>{" "}
|
</a>{" "}
|
||||||
or email{" "}
|
or email{" "}
|
||||||
<a
|
<a
|
||||||
|
66
frontend/src/pages/FineTuning/Banner/index.jsx
Normal file
66
frontend/src/pages/FineTuning/Banner/index.jsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import useUser from "@/hooks/useUser";
|
||||||
|
import FineTuning from "@/models/experimental/fineTuning";
|
||||||
|
import { createPortal } from "react-dom";
|
||||||
|
import { Sparkle } from "@phosphor-icons/react";
|
||||||
|
import { Link, useLocation } from "react-router-dom";
|
||||||
|
import paths from "@/utils/paths";
|
||||||
|
|
||||||
|
export function FineTuningAlert() {
|
||||||
|
const { user } = useUser();
|
||||||
|
const location = useLocation();
|
||||||
|
const [className, setClassName] = useState("top-banner");
|
||||||
|
const [isEligible, setIsEligible] = useState(false);
|
||||||
|
|
||||||
|
function dismissAlert() {
|
||||||
|
setClassName("rm-top-banner");
|
||||||
|
window?.localStorage?.setItem(FineTuning.cacheKeys.dismissed_cta, "1");
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsEligible(false);
|
||||||
|
}, 550);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!FineTuning.canAlert(user)) return;
|
||||||
|
if (
|
||||||
|
location.pathname === paths.orderFineTune() ||
|
||||||
|
location.pathname === paths.settings.chats()
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
FineTuning.checkEligibility()
|
||||||
|
.then((eligible) => setIsEligible(eligible))
|
||||||
|
.catch(() => null);
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
|
if (!isEligible) return null;
|
||||||
|
return createPortal(
|
||||||
|
<div
|
||||||
|
className={`fixed ${className} left-0 right-0 h-14 bg-orange-400 flex items-center justify-end px-4 z-[9999]`}
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
onClick={dismissAlert}
|
||||||
|
to={paths.orderFineTune()}
|
||||||
|
className="grow w-full h-full ml-4 py-1"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col items-center w-full">
|
||||||
|
<div className="flex w-full justify-center items-center gap-x-2">
|
||||||
|
<Sparkle size={20} className="text-white" />
|
||||||
|
<p className="text-white font-medium text-lg">
|
||||||
|
You have enough data for a fine-tune!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-white">click to learn more →</p>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
<div className="flex items-center gap-x-2 shrink-0">
|
||||||
|
<button
|
||||||
|
onClick={dismissAlert}
|
||||||
|
className="border-none text-white font-medium text-sm px-[10px] py-[6px] rounded-md bg-white/5 hover:bg-white/10"
|
||||||
|
>
|
||||||
|
Dismiss
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
document.getElementById("root")
|
||||||
|
);
|
||||||
|
}
|
147
frontend/src/pages/FineTuning/Steps/Confirmation/index.jsx
Normal file
147
frontend/src/pages/FineTuning/Steps/Confirmation/index.jsx
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
import FineTuning from "@/models/experimental/fineTuning";
|
||||||
|
import { dollarFormat } from "@/utils/numbers";
|
||||||
|
import showToast from "@/utils/toast";
|
||||||
|
import { CheckCircle } from "@phosphor-icons/react";
|
||||||
|
import { useState } from "react";
|
||||||
|
import FineTuningSteps from "../index";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {{settings: import("../index").OrderSettings}} param0
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export default function Confirmation({ settings, setSettings, setStep }) {
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
async function handleCheckout() {
|
||||||
|
setLoading(true);
|
||||||
|
const data = await FineTuning.createOrder({
|
||||||
|
email: settings.email,
|
||||||
|
baseModel: settings.baseModel,
|
||||||
|
modelName: settings.modelName,
|
||||||
|
trainingData: {
|
||||||
|
slugs: settings.trainingData.slugs,
|
||||||
|
feedback: settings.trainingData.feedback,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
setLoading(false);
|
||||||
|
showToast("Could not generate new order.", "error", { clear: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.open(data.checkoutUrl, "_blank");
|
||||||
|
setSettings((prev) => {
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
jobId: data.jobId,
|
||||||
|
checkoutUrl: data.checkoutUrl,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setStep(FineTuningSteps.confirmation.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
||||||
|
<div className="bg-[#303237] text-white rounded-xl flex-1 p-4 flex flex-col justify-between">
|
||||||
|
<div className="w-full flex flex-col gap-y-4">
|
||||||
|
<h2 className="text-xl text-white font-semibold">Confirm & Submit</h2>
|
||||||
|
<p>
|
||||||
|
Below are your fine-tuning order details. If you have any questions
|
||||||
|
before or after ordering your fine-tune you can{" "}
|
||||||
|
<a
|
||||||
|
href="https://docs.useanything.com/fine-tuning/overview"
|
||||||
|
target="_blank"
|
||||||
|
className="underline"
|
||||||
|
>
|
||||||
|
checkout the fine-tuning FAQ
|
||||||
|
</a>{" "}
|
||||||
|
or email{" "}
|
||||||
|
<a className="underline" href="mailto:team@mintplexlabs.com">
|
||||||
|
team@mintplexlabs.com
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
<div className="p-2 bg-zinc-800 text-white font-mono flex flex-col gap-y-2 h-full rounded-lg">
|
||||||
|
<div className="flex items-center gap-x-1 text-sm">
|
||||||
|
<p className="">Contact e-mail:</p>
|
||||||
|
<p className="font-thin">{settings.email}</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-x-1 text-sm">
|
||||||
|
<p className="">Base LLM:</p>
|
||||||
|
<p className="font-thin">{settings.baseModel}</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-x-1 text-sm">
|
||||||
|
<p className="">Output model name:</p>
|
||||||
|
<p className="font-thin">"{settings.modelName}"</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-y-1 text-sm">
|
||||||
|
<div className="flex items-center gap-x-1">
|
||||||
|
<p className="">Training on workspaces:</p>
|
||||||
|
{settings.trainingData.slugs.map((slug, i) => {
|
||||||
|
return (
|
||||||
|
<p key={slug} className="font-thin">
|
||||||
|
"{slug}"
|
||||||
|
{i !== settings.trainingData.slugs.length - 1 ? "," : ""}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
{settings.trainingData.feedback === true ? (
|
||||||
|
<p className="underline">
|
||||||
|
training on <b>positive-feedback chats only</b>.
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<p className="underline">
|
||||||
|
training on <b>all chats</b>.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<div className="flex items-center gap-x-1 text-sm">
|
||||||
|
<CheckCircle className="text-green-300" />
|
||||||
|
<p className="font-thin">Agreed to Terms and Conditions</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-x-1 text-sm">
|
||||||
|
<CheckCircle className="text-green-300" />
|
||||||
|
<p className="font-thin">Understand privacy & data handling</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-x-1 text-sm">
|
||||||
|
<CheckCircle className="text-green-300" />
|
||||||
|
<p className="font-thin">Agreed to Fulfillment terms</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-x-1 text-lg border-t-[2px] border-white/40 pt-2 mb-0">
|
||||||
|
<p className="">Total one-time cost:</p>
|
||||||
|
<p className="font-thin">
|
||||||
|
{dollarFormat(settings.tuningInfo.pricing.usd)}
|
||||||
|
<sup>*</sup>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<p className="m-0 p-0 text-xs text-white/60 font-mono">
|
||||||
|
<sup>*</sup> price does not include any coupons, incentives, or
|
||||||
|
discounts you can apply at checkout.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
Once you proceed to checkout, if you do not complete this purchase
|
||||||
|
your data will be deleted from our servers within 1 hour of
|
||||||
|
abandonment of the creation of the checkout in accordance to our
|
||||||
|
privacy and data handling policy.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
disabled={loading}
|
||||||
|
onClick={handleCheckout}
|
||||||
|
type="button"
|
||||||
|
className="mt-8 w-full py-2 text-center text-black bg-white hover:bg-white/80 border-none rounded-lg"
|
||||||
|
>
|
||||||
|
{loading ? <>Generating order...</> : <>Start Training →</>}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
295
frontend/src/pages/FineTuning/Steps/DataUpload/index.jsx
Normal file
295
frontend/src/pages/FineTuning/Steps/DataUpload/index.jsx
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import FineTuning from "@/models/experimental/fineTuning";
|
||||||
|
import Workspace from "@/models/workspace";
|
||||||
|
import { CheckCircle, Warning, X } from "@phosphor-icons/react";
|
||||||
|
import FineTuningSteps from "..";
|
||||||
|
|
||||||
|
export default function DataUpload({ setSettings, setStep }) {
|
||||||
|
const [workspaces, setWorkspaces] = useState([]);
|
||||||
|
const [dataFilters, setDataFilters] = useState({
|
||||||
|
workspaces: [],
|
||||||
|
feedback: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
Workspace.all()
|
||||||
|
.then((workspaces) => {
|
||||||
|
const workspaceOpts = workspaces.map((ws) => {
|
||||||
|
return { slug: ws.slug, name: ws.name };
|
||||||
|
});
|
||||||
|
setWorkspaces(workspaceOpts);
|
||||||
|
setDataFilters((prev) => {
|
||||||
|
return { ...prev, workspaces: workspaceOpts };
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => null);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
async function handleSubmit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
setSettings((prev) => {
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
trainingData: {
|
||||||
|
slugs: dataFilters.workspaces.map((ws) => ws.slug),
|
||||||
|
feedback: dataFilters.feedback,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setStep(FineTuningSteps["data-selection"].next());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
||||||
|
<div className="bg-[#303237] text-white rounded-xl flex-1 p-4">
|
||||||
|
<form
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
className="flex flex-col justify-between h-full"
|
||||||
|
>
|
||||||
|
<div className="w-full flex flex-col gap-y-4">
|
||||||
|
<h2 className="text-xl text-white font-semibold">
|
||||||
|
Select your training dataset.
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
This is the data your model will be trained and tuned on. This is
|
||||||
|
a critical step and you should always train on the exact
|
||||||
|
information you want the model to inherit. By default, AnythingLLM
|
||||||
|
will use all chats, but you can filter chats by workspace and even
|
||||||
|
limit training to chats which users have left a positive feedback
|
||||||
|
indication on (thumbs up).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex flex-col pr-10">
|
||||||
|
<div className="flex flex-col gap-y-1 mb-4">
|
||||||
|
<label className="text-white text-sm font-bold">
|
||||||
|
Only use positive responses
|
||||||
|
</label>
|
||||||
|
<p className="text-xs font-normal text-white/80">
|
||||||
|
Enabling this toggle will filter your dataset to only use
|
||||||
|
"positive" responses that were marked during chatting.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<label className="relative inline-flex cursor-pointer items-center w-fit">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
onClick={() =>
|
||||||
|
setDataFilters((prev) => {
|
||||||
|
return { ...prev, feedback: !prev.feedback };
|
||||||
|
})
|
||||||
|
}
|
||||||
|
checked={dataFilters.feedback}
|
||||||
|
className="peer sr-only pointer-events-none"
|
||||||
|
/>
|
||||||
|
<div className="pointer-events-none peer h-6 w-11 rounded-full bg-stone-400 after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border after:border-gray-600 after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-lime-300 peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-0 peer-focus:ring-none"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col pr-10">
|
||||||
|
<div className="flex flex-col gap-y-1 mb-4">
|
||||||
|
<label className="text-white text-sm font-bold">
|
||||||
|
Selected Workspaces
|
||||||
|
</label>
|
||||||
|
<p className="text-xs font-normal text-white/80">
|
||||||
|
You training data will be limited to these workspaces.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<WorkspaceSelector
|
||||||
|
workspaces={workspaces}
|
||||||
|
selectedWorkspaces={dataFilters.workspaces}
|
||||||
|
setDataFilters={setDataFilters}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<DatasetSummary
|
||||||
|
workspaces={dataFilters.workspaces}
|
||||||
|
feedback={dataFilters.feedback}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="mt-20 w-full py-2 text-center text-white hover:bg-primary-button border-none rounded-lg"
|
||||||
|
>
|
||||||
|
Proceed to Confirmation →
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function WorkspaceSelector({
|
||||||
|
workspaces = [],
|
||||||
|
selectedWorkspaces = [],
|
||||||
|
setDataFilters,
|
||||||
|
}) {
|
||||||
|
const [query, setQuery] = useState("");
|
||||||
|
const [showSuggestions, setShowSuggestions] = useState(false);
|
||||||
|
const availableWorkspaces = workspaces.filter(
|
||||||
|
(ws) =>
|
||||||
|
!selectedWorkspaces.find((selectedWs) => selectedWs.slug === ws.slug)
|
||||||
|
);
|
||||||
|
|
||||||
|
function handleAddWorkspace(workspace) {
|
||||||
|
setDataFilters((prev) => {
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
workspaces: [...prev.workspaces, workspace],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setQuery("");
|
||||||
|
setShowSuggestions(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRemoveWorkspace(workspace) {
|
||||||
|
setDataFilters((prev) => {
|
||||||
|
const filtered = prev.workspaces.filter(
|
||||||
|
(ws) => ws.slug !== workspace.slug
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
workspaces: filtered,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setQuery("");
|
||||||
|
setShowSuggestions(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center">
|
||||||
|
<div className="w-full h-fit">
|
||||||
|
<div className="w-full relative z-1">
|
||||||
|
<div className="p-1 flex border border-white/40 bg-zinc-800 rounded">
|
||||||
|
<div className="flex flex-auto flex-wrap">
|
||||||
|
{selectedWorkspaces.map((workspace) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={workspace.slug}
|
||||||
|
className="flex gap-x-1 justify-center items-center m-1 font-medium py-1 px-2 bg-zinc-800 rounded-full text-white/40 bg-white/10 border border-white/40 "
|
||||||
|
>
|
||||||
|
<div className="text-xs font-normal text-white leading-none max-w-full flex-initial">
|
||||||
|
{workspace.name}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-auto flex-row-reverse">
|
||||||
|
<button
|
||||||
|
onClick={() => handleRemoveWorkspace(workspace)}
|
||||||
|
type="button"
|
||||||
|
className="hover:text-red-500"
|
||||||
|
>
|
||||||
|
<X size={14} weight="bold" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<div className="flex-1">
|
||||||
|
<input
|
||||||
|
value={query}
|
||||||
|
onChange={(e) => setQuery(e.target.value)}
|
||||||
|
onFocus={() => setShowSuggestions(true)}
|
||||||
|
onBlur={() =>
|
||||||
|
setTimeout(() => {
|
||||||
|
setShowSuggestions(false);
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
placeholder="Enter a workspace name"
|
||||||
|
className="w-[200px] bg-transparent p-1 px-2 appearance-none outline-none h-full w-full text-white"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{showSuggestions && (
|
||||||
|
<div className="w-full flex relative">
|
||||||
|
<div className="w-full absolute top-0 z-20">
|
||||||
|
<WorkspaceSuggestions
|
||||||
|
availableWorkspaces={availableWorkspaces}
|
||||||
|
addWorkspace={handleAddWorkspace}
|
||||||
|
query={query}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function WorkspaceSuggestions({
|
||||||
|
availableWorkspaces = [],
|
||||||
|
addWorkspace,
|
||||||
|
query = "",
|
||||||
|
}) {
|
||||||
|
if (availableWorkspaces.length === 0) {
|
||||||
|
return (
|
||||||
|
<div className="w-full mt-[2px] bg-zinc-800 border border-white/40 top-[45px] h-40 rounded-lg p-2 text-sm">
|
||||||
|
<p className="text-center text-white/40">
|
||||||
|
no workspaces available to select.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const filteredWorkspace = !!query
|
||||||
|
? availableWorkspaces.filter((ws) => {
|
||||||
|
return (
|
||||||
|
ws.slug.toLowerCase().includes(query.toLowerCase()) ||
|
||||||
|
ws.name.toLowerCase().includes(query.toLowerCase())
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: availableWorkspaces;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full mt-[2px] bg-zinc-800 border border-white/40 top-[45px] h-40 rounded-lg p-2 text-sm flex flex-col gap-y-1 justify-start overflow-y-scroll">
|
||||||
|
{filteredWorkspace.map((workspace) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={workspace.slug}
|
||||||
|
onClick={() => addWorkspace(workspace)}
|
||||||
|
type="button"
|
||||||
|
className="text-left text-white hover:bg-white/10 rounded-lg p-1"
|
||||||
|
>
|
||||||
|
{workspace.name}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function DatasetSummary({ workspaces = [], feedback = null }) {
|
||||||
|
const [stats, setStats] = useState({ count: null, recommendedMin: 50 });
|
||||||
|
useEffect(() => {
|
||||||
|
function getStats() {
|
||||||
|
const slugs = workspaces?.map((ws) => ws.slug);
|
||||||
|
if (!slugs || slugs.length === 0) return;
|
||||||
|
|
||||||
|
FineTuning.datasetStat({ slugs, feedback })
|
||||||
|
.then((stats) => setStats(stats))
|
||||||
|
.catch((e) => null);
|
||||||
|
}
|
||||||
|
getStats();
|
||||||
|
}, [workspaces, feedback]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-zinc-800 text-white/80 p-2 rounded-lg font-mono">
|
||||||
|
<p>Training dataset size: {stats.count ?? "Unknown"}</p>
|
||||||
|
{stats.count < stats.recommendedMin ? (
|
||||||
|
<div className="flex items-center gap-x-1 text-red-500 text-sm p-1 rounded-lg bg-red-500/20 w-fit my-2">
|
||||||
|
<Warning />
|
||||||
|
<p>
|
||||||
|
Your dataset is below the recommended minimum of{" "}
|
||||||
|
{stats.recommendedMin}! You may see no impact from a fine-tune.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex items-center gap-x-1 text-green-500 text-sm p-1 rounded-lg bg-green-500/20 w-fit my-2">
|
||||||
|
<CheckCircle />
|
||||||
|
<p>
|
||||||
|
Your dataset is large enough that you should see good results from a
|
||||||
|
fine-tune.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
129
frontend/src/pages/FineTuning/Steps/FulfillmentPolicy/index.jsx
Normal file
129
frontend/src/pages/FineTuning/Steps/FulfillmentPolicy/index.jsx
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
import FineTuningSteps from "..";
|
||||||
|
|
||||||
|
export default function Fulfillment({ setSettings, setStep }) {
|
||||||
|
const handleAccept = () => {
|
||||||
|
setSettings((prev) => {
|
||||||
|
return { ...prev, agreedToTerms: true };
|
||||||
|
});
|
||||||
|
setStep(FineTuningSteps.fulfillment.next());
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
||||||
|
<div className="bg-[#303237] text-white rounded-xl flex-1 p-4">
|
||||||
|
<div className="w-full flex flex-col gap-y-4">
|
||||||
|
<h2 className="text-xl text-white font-semibold">
|
||||||
|
Fulfillment Policy
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
Fulfillment of a fine-tune model is straight-forward. We do not host
|
||||||
|
your model. We provide you a download link to run the model in a
|
||||||
|
standard format where ever you run local LLMs
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-col gap-y-2 text-white/75 text-sm border p-2 border-white rounded-lg font-mono h-[60vh] overflow-y-scroll">
|
||||||
|
<h1 class="text-white/80 text-lg font-semibold">
|
||||||
|
Fulfillment Terms
|
||||||
|
</h1>
|
||||||
|
<p>
|
||||||
|
<strong>Last updated: July 15, 2024</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
These fulfillment terms outline the agreement between Mintplex
|
||||||
|
Labs Inc. (“Company,” “we,” “us,” or “our”) and the customer
|
||||||
|
regarding the creation and delivery of fine-tuned models.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 class="text-white/80 text-base font-semibold">
|
||||||
|
Delivery of Model
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
Upon completion of a fine-tuning job, we will deliver a download
|
||||||
|
link to a .gguf model file suitable for LLM text inferencing. The
|
||||||
|
customer acknowledges that this exchange is strictly transactional
|
||||||
|
and non-recurring. Once the model file is delivered, the agreement
|
||||||
|
is considered concluded and will be ineligible for a refund.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 class="text-white/80 text-base font-semibold">Support</h2>
|
||||||
|
<p>
|
||||||
|
Please note that the delivery of the model does not include any
|
||||||
|
dedicated support. Customers are encouraged to refer to available
|
||||||
|
documentation and resources for guidance on using the model.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 class="text-white/80 text-base font-semibold">
|
||||||
|
Requesting Download Links
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
Customers may request refreshed download links from
|
||||||
|
my.mintplexlabs.com as long as the model is retained in our cloud
|
||||||
|
storage. We will retain a model in our storage for a maximum of 3
|
||||||
|
months or until the customer requests its removal. All download
|
||||||
|
links are valid for 24 hours.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 class="text-white/80 text-base font-semibold">
|
||||||
|
Cancellation and Refunds
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
Mintplex Labs Inc. reserves the right to cancel any fine-tuning
|
||||||
|
job at our discretion. In the event of a cancellation, a refund
|
||||||
|
may be issued. Additionally, we reserve the right to deny a
|
||||||
|
payment from the Customer or issue refunds for any reason without
|
||||||
|
cause or notice to the Customer.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 class="text-white/80 text-base font-semibold">No Guarantees</h2>
|
||||||
|
<p>
|
||||||
|
Mintplex Labs Inc. makes <strong>NO GUARANTEES</strong> regarding
|
||||||
|
the resulting model's output, functionality, speed, or
|
||||||
|
compatibility with your tools, infrastructure and devices. Refund
|
||||||
|
requests of this nature are not eligible for refunds.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Models are delivered and accepted in "As-Is" condition. All
|
||||||
|
delivered model and output files are deemed final and
|
||||||
|
non-refundable for any reason after training is complete and a
|
||||||
|
model has been generated.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 class="text-white/80 text-base font-semibold">Payment Terms</h2>
|
||||||
|
<p>
|
||||||
|
All payments are required prior to the commencement of the
|
||||||
|
fine-tuning process. Customers are responsible for ensuring that
|
||||||
|
valid payment information is provided. Checkout sessions not
|
||||||
|
completed within 1 hour of creation will be considered as
|
||||||
|
abandoned and will be deleted from our system.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 class="text-white/80 text-base font-semibold">
|
||||||
|
Denial of Service for Payment Reasons
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
Mintplex Labs Inc. reserves the right to deny service to any
|
||||||
|
customer with an outstanding balance or invalid payment
|
||||||
|
information. If any discrepancies arise regarding payment or
|
||||||
|
usage, we may suspend services until the matter is resolved.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 class="text-white/80 text-base font-semibold">Contact</h2>
|
||||||
|
<p>
|
||||||
|
For any questions related to payment or fulfillment of services,
|
||||||
|
please contact us at{" "}
|
||||||
|
<a href="mailto:team@mintplexlabs.com">team@mintplexlabs.com</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleAccept}
|
||||||
|
type="button"
|
||||||
|
className="mt-8 w-full py-2 text-center text-white hover:bg-primary-button border-none rounded-lg"
|
||||||
|
>
|
||||||
|
Agree and continue →
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
110
frontend/src/pages/FineTuning/Steps/Introduction/index.jsx
Normal file
110
frontend/src/pages/FineTuning/Steps/Introduction/index.jsx
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import { CheckCircle, XCircle } from "@phosphor-icons/react";
|
||||||
|
import FineTuningSteps from "..";
|
||||||
|
|
||||||
|
export default function Introduction({ setSettings, setStep }) {
|
||||||
|
const handleAccept = () => {
|
||||||
|
setSettings((prev) => {
|
||||||
|
return { ...prev, agreedToTerms: true };
|
||||||
|
});
|
||||||
|
setStep(FineTuningSteps.intro.next());
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
||||||
|
<div className="bg-[#303237] text-white rounded-xl flex-1 p-4">
|
||||||
|
<div className="w-full flex flex-col gap-y-4">
|
||||||
|
<h2 className="text-xl text-white font-semibold">
|
||||||
|
What is a "Fine-Tuned" model?
|
||||||
|
</h2>
|
||||||
|
<div className="flex flex-col gap-y-2 text-white/80">
|
||||||
|
<p>
|
||||||
|
Fine-tuned models are basically "customized"
|
||||||
|
Language-Learning-Models (LLMs). These can be based on popular
|
||||||
|
open-source <b>foundational</b> models like LLama3 8B or even some
|
||||||
|
closed source models like GPT-3.5.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Typically, you would use an open-source model - you probably are
|
||||||
|
using one right now with AnythingLLM!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
When you create a custom fine-tune with AnythingLLM we will train
|
||||||
|
a custom base model on your specific data already inside of this
|
||||||
|
AnythingLLM instance and give you back a <code>GGUF</code> file
|
||||||
|
you can then load back into tools like Ollama, LMStudio, and
|
||||||
|
anywhere else you use local LLMs.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-y-2 text-white/80">
|
||||||
|
<h3 className="text-lg text-white font-semibold">
|
||||||
|
When should I get a fine-tuned model?
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
Fine-tuned models are perfect for when you need any of the
|
||||||
|
following
|
||||||
|
</p>
|
||||||
|
<ul className="flex flex-col gap-y-1">
|
||||||
|
<li className="flex items-center gap-x-1">
|
||||||
|
<CheckCircle className="text-green-300" /> Setting the style,
|
||||||
|
tone, format, or other qualitative aspects without prompting
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center gap-x-1">
|
||||||
|
<CheckCircle className="text-green-300" /> Improving reliability
|
||||||
|
at producing a desired output
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center gap-x-1">
|
||||||
|
<CheckCircle className="text-green-300" /> Correcting failures
|
||||||
|
to follow complex prompts, citations, or lack of background
|
||||||
|
knowledge
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center gap-x-1">
|
||||||
|
<CheckCircle className="text-green-300" /> You want to run this
|
||||||
|
model privately or offline
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-y-2 text-white/80">
|
||||||
|
<h3 className="text-lg text-white font-semibold">
|
||||||
|
What are fine-tunes bad for?
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
Fine-tuned models powerful, but they are not the "silver bullet"
|
||||||
|
to any issues you have with RAG currently. Some notable
|
||||||
|
limitations are
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li className="flex items-center gap-x-1">
|
||||||
|
<XCircle className="text-red-300" /> You need perfect recall of
|
||||||
|
some piece of literature or reference document
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center gap-x-1">
|
||||||
|
<XCircle className="text-red-300" /> You want your model to have
|
||||||
|
perfect memory or recollection
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-y-2 text-white/80">
|
||||||
|
<p>
|
||||||
|
In summary, if you are getting good results with RAG currently,
|
||||||
|
creating a fine-tune can squeeze <b>even more performance</b> out
|
||||||
|
of a model. Fine-Tunes are are for improving response quality and
|
||||||
|
general responses, but they are <b>not for knowledge recall</b> -
|
||||||
|
that is what RAG is for! Together, it is a powerful combination.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleAccept}
|
||||||
|
type="button"
|
||||||
|
className="mt-8 w-full py-2 text-center text-white hover:bg-primary-button border-none rounded-lg"
|
||||||
|
>
|
||||||
|
Start a fine-tune →
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
138
frontend/src/pages/FineTuning/Steps/OrderDetails/index.jsx
Normal file
138
frontend/src/pages/FineTuning/Steps/OrderDetails/index.jsx
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
import FineTuning from "@/models/experimental/fineTuning";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import FineTuningSteps from "..";
|
||||||
|
import { CircleNotch } from "@phosphor-icons/react";
|
||||||
|
|
||||||
|
export default function OrderDetails({ setSettings, setStep }) {
|
||||||
|
const [info, setInfo] = useState({});
|
||||||
|
useEffect(() => {
|
||||||
|
FineTuning.info()
|
||||||
|
.then((res) => {
|
||||||
|
setInfo(res);
|
||||||
|
setSettings((prev) => {
|
||||||
|
return { ...prev, tuningInfo: res };
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => setInfo({}));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
async function handleSubmit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const form = new FormData(e.target);
|
||||||
|
setSettings((prev) => {
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
email: form.get("email"),
|
||||||
|
baseModel: form.get("baseModel"),
|
||||||
|
modelName: form.get("modelName"),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setStep(FineTuningSteps["order-details"].next());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
||||||
|
<div className="bg-[#303237] text-white rounded-xl flex-1 p-4">
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="w-full flex flex-col gap-y-4">
|
||||||
|
<h2 className="text-xl text-white font-semibold">
|
||||||
|
Time to create your fine tune!
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
Creating a model is quite simple. Currently we have a limited base
|
||||||
|
model selection, however in the future we plan to expand support
|
||||||
|
to many more foundational models.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex flex-col pr-10">
|
||||||
|
<div className="flex flex-col gap-y-1 mb-4">
|
||||||
|
<label className="text-white text-sm font-bold">
|
||||||
|
Account e-mail
|
||||||
|
</label>
|
||||||
|
<p className="text-xs font-normal text-white/80">
|
||||||
|
This e-mail is where you will receive all order information
|
||||||
|
and updates. This e-mail <b>must be accurate</b> or else we
|
||||||
|
won't be able to contact you with your fine-tuned model!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
name="email"
|
||||||
|
className="border-none bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
|
||||||
|
placeholder="jdoe@example.com"
|
||||||
|
required={true}
|
||||||
|
autoComplete="off"
|
||||||
|
spellCheck={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col pr-10">
|
||||||
|
<div className="flex flex-col gap-y-1 mb-4">
|
||||||
|
<label className="text-white text-sm font-bold">
|
||||||
|
Preferred Base Model
|
||||||
|
</label>
|
||||||
|
<p className="text-xs font-normal text-white/80">
|
||||||
|
This is the foundational model your fine-tune will be based
|
||||||
|
on. We recommend Llama 3 8B.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{info.hasOwnProperty("availableBaseModels") ? (
|
||||||
|
<select
|
||||||
|
name="baseModel"
|
||||||
|
required={true}
|
||||||
|
className="border-none bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-fit p-2.5"
|
||||||
|
>
|
||||||
|
<option disabled="true" selected="true" value="">
|
||||||
|
-- select a base model --
|
||||||
|
</option>
|
||||||
|
<optgroup label="Available base models">
|
||||||
|
{(info?.availableBaseModels || []).map((model) => {
|
||||||
|
return (
|
||||||
|
<option key={model} value={model}>
|
||||||
|
{model}
|
||||||
|
</option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
) : (
|
||||||
|
<div className="flex items-center gap-x-2 text-white/80 text-sm">
|
||||||
|
<CircleNotch className="animate-spin" size={12} />
|
||||||
|
<p>fetching available models...</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col pr-10">
|
||||||
|
<div className="flex flex-col gap-y-1 mb-4">
|
||||||
|
<label className="text-white text-sm font-bold">
|
||||||
|
Model name
|
||||||
|
</label>
|
||||||
|
<p className="text-xs font-normal text-white/80">
|
||||||
|
What would you like to call your model? This has no impact on
|
||||||
|
its output or training and is only used for how we communicate
|
||||||
|
with you about the model.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="modelName"
|
||||||
|
className="border-none bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
|
||||||
|
placeholder="My really cool model!"
|
||||||
|
required={true}
|
||||||
|
autoComplete="off"
|
||||||
|
spellCheck={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="mt-8 w-full py-2 text-center text-white hover:bg-primary-button border-none rounded-lg"
|
||||||
|
>
|
||||||
|
Proceed to data selection →
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
69
frontend/src/pages/FineTuning/Steps/OrderPlaced/index.jsx
Normal file
69
frontend/src/pages/FineTuning/Steps/OrderPlaced/index.jsx
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
export default function OrderPlaced({ settings }) {
|
||||||
|
return (
|
||||||
|
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
||||||
|
<div className="bg-[#303237] text-white rounded-xl flex-1 p-4">
|
||||||
|
<div className="w-full flex flex-col gap-y-4">
|
||||||
|
<h2 className="text-xl text-white font-semibold">
|
||||||
|
Your order is placed!
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div className="">
|
||||||
|
<p>
|
||||||
|
Your fine-tune will begin once payment is complete. If the payment
|
||||||
|
window did not automatically open - your checkout link is below.
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href={settings.checkoutUrl}
|
||||||
|
target="_blank"
|
||||||
|
className="text-xs font-mono text-white/60 underline"
|
||||||
|
>
|
||||||
|
{new URL(settings.checkoutUrl).origin}
|
||||||
|
</a>
|
||||||
|
<p className="text-xs font-mono text-white/80">
|
||||||
|
Your fine-tune does not begin until this payment is completed.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="">
|
||||||
|
<p className="font-mono text-white/80 text-sm">
|
||||||
|
Reference: {settings.jobId}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs font-mono text-white/80">
|
||||||
|
This reference id is how we will communicate with you about your
|
||||||
|
fine-tune training status. <b>Save this reference id.</b>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="">
|
||||||
|
<p className="font-mono text-white/80 text-sm">
|
||||||
|
Contact: {settings.email}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs font-mono text-white/80">
|
||||||
|
Check the email above for order confirmation, status updates, and
|
||||||
|
more. Mintplex Labs will only contact you about your order via
|
||||||
|
email.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="font-mono text-white/80 text-sm flex items-center gap-x-2">
|
||||||
|
<a
|
||||||
|
href="https://docs.useanything.com/fine-tuning/overview"
|
||||||
|
target="_blank"
|
||||||
|
className="underline"
|
||||||
|
>
|
||||||
|
Documentation
|
||||||
|
</a>
|
||||||
|
<a href="mailto:team@mintplexlabs.com" className="underline">
|
||||||
|
Contact support
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-xs font-mono text-white/80">
|
||||||
|
You can close this window or navigate away once you see the
|
||||||
|
confirmation email in your inbox.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
233
frontend/src/pages/FineTuning/Steps/Privacy/index.jsx
Normal file
233
frontend/src/pages/FineTuning/Steps/Privacy/index.jsx
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
import FineTuningSteps from "..";
|
||||||
|
|
||||||
|
export default function PrivacyHandling({ setSettings, setStep }) {
|
||||||
|
const handleAccept = () => {
|
||||||
|
setSettings((prev) => {
|
||||||
|
return { ...prev, agreedToPrivacy: true };
|
||||||
|
});
|
||||||
|
setStep(FineTuningSteps.privacy.next());
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
||||||
|
<div className="bg-[#303237] text-white rounded-xl flex-1 p-4">
|
||||||
|
<div className="w-full flex flex-col gap-y-4">
|
||||||
|
<h2 className="text-xl text-white font-semibold">
|
||||||
|
Data Handling Policy & Privacy
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
Please accept the terms and conditions to continue with creation and
|
||||||
|
ordering of a fine-tune model. We take the handling of your data
|
||||||
|
very seriously and will only use your uploaded data for training the
|
||||||
|
model, after the model is created or the order is concluded,
|
||||||
|
completed, or canceled your information is automatically and
|
||||||
|
permanently deleted.
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-col gap-y-2 text-white/75 text-sm border p-2 border-white rounded-lg font-mono h-[60vh] overflow-y-scroll">
|
||||||
|
<h1 class="text-white/80 text-lg font-semibold">Privacy Policy</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>Mintplex Labs Inc.</strong>
|
||||||
|
</p>
|
||||||
|
<p>Effective Date: July 15, 2024</p>
|
||||||
|
|
||||||
|
<h2 class="text-white/80 text-base font-semibold">
|
||||||
|
1. Introduction
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
Welcome to Mintplex Labs Inc. ("we", "our", "us"). We are
|
||||||
|
committed to protecting your privacy and ensuring the security of
|
||||||
|
your personal information. This Privacy Policy describes how we
|
||||||
|
collect, use, and protect your information when you use our
|
||||||
|
services.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 class="text-white/80 text-base font-semibold">
|
||||||
|
2. Information We Collect
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
When you place an order with us for tuning and large language
|
||||||
|
model (LLM) fulfillment, we collect certain personal information
|
||||||
|
from you, including but not limited to:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>Email address</li>
|
||||||
|
<li>Payment information</li>
|
||||||
|
<li>Uploaded training data</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2 class="text-white/80 text-base font-semibold">
|
||||||
|
3. Use of Information
|
||||||
|
</h2>
|
||||||
|
<p>We use the information we collect for the following purposes:</p>
|
||||||
|
<ul>
|
||||||
|
<li>To process and fulfill your order</li>
|
||||||
|
<li>To communicate with you regarding your order</li>
|
||||||
|
<li>To improve our services</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2 class="text-white/80 text-base font-semibold">
|
||||||
|
4. Data Retention and Deletion
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
Uploaded training data is only retained for the duration of the
|
||||||
|
model training. Upon training completion, failure, or order
|
||||||
|
cancellation, the user data is permanently deleted from our
|
||||||
|
storage.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If you partially complete the order flow and do not finalize your
|
||||||
|
order, any details and information associated with your order will
|
||||||
|
be deleted 1 hour from abandonment.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
After you confirm receipt of your resulting model files, you can
|
||||||
|
request us to delete your model from our storage at any time.
|
||||||
|
Additionally, we may proactively reach out to you to confirm that
|
||||||
|
you have received your model so we can delete it from storage. Our
|
||||||
|
model file retention policy is 3 months, after which we will
|
||||||
|
contact you to confirm receipt so we can remove the model from our
|
||||||
|
storage.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 class="text-white/80 text-base font-semibold">
|
||||||
|
5. Data Storage and Security
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
Our cloud storage provider is AWS. We have implement standard
|
||||||
|
encryption and protection policies to ensure the security of your
|
||||||
|
data. The storage solution has no public access, and all requests
|
||||||
|
for download URLs are pre-validated and signed by a minimal trust
|
||||||
|
program. Download URLs for the model file and associated outputs
|
||||||
|
are valid for 24 hours at a time. After expiration you can produce
|
||||||
|
refreshed links from https://my.mintplexlabs.com using the same
|
||||||
|
e-mail you used during checkout.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 class="text-white/80 text-base font-semibold">
|
||||||
|
6. Payment Processing
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
We use Stripe as our payment processor. Your email may be shared
|
||||||
|
with Stripe for customer service and payment management purposes.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 class="text-white/80 text-base font-semibold">
|
||||||
|
7. Data Sharing
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
We do not sell or share your personal information with third
|
||||||
|
parties except as necessary to provide our services, comply with
|
||||||
|
legal obligations, or protect our rights.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 class="text-white/80 text-base font-semibold">
|
||||||
|
8. Your Rights
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
You have the right to access, correct, or delete your personal
|
||||||
|
information. If you wish to exercise these rights, please contact
|
||||||
|
us at{" "}
|
||||||
|
<a href="mailto:team@mintplexlabs.com">team@mintplexlabs.com</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 class="text-white/80 text-base font-semibold">
|
||||||
|
9. California Privacy Rights
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
Under the California Consumer Privacy Act as amended by the
|
||||||
|
California Privacy Rights Act (the “CCPA”), California residents
|
||||||
|
have additional rights beyond what is set out in this privacy
|
||||||
|
notice:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<strong>Right to Know:</strong> You have the right to request
|
||||||
|
information about the categories and specific pieces of personal
|
||||||
|
information we have collected about you, as well as the
|
||||||
|
categories of sources from which the information is collected,
|
||||||
|
the purpose for collecting such information, and the categories
|
||||||
|
of third parties with whom we share personal information.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Right to Delete:</strong> You have the right to request
|
||||||
|
the deletion of your personal information, subject to certain
|
||||||
|
exceptions.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Right to Correct:</strong> You have the right to request
|
||||||
|
the correction of inaccurate personal information that we have
|
||||||
|
about you.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Right to Opt-Out:</strong> You have the right to opt-out
|
||||||
|
of the sale of your personal information. Note, however, that we
|
||||||
|
do not sell your personal information.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Right to Non-Discrimination:</strong> You have the right
|
||||||
|
not to receive discriminatory treatment for exercising any of
|
||||||
|
your CCPA rights.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
<strong>Submitting a Request:</strong>
|
||||||
|
<br />
|
||||||
|
You may submit a request to know, delete, or correct your personal
|
||||||
|
information by contacting us at{" "}
|
||||||
|
<a href="mailto:team@mintplexlabs.com">team@mintplexlabs.com</a>.
|
||||||
|
We will confirm your identity before processing your request and
|
||||||
|
respond within 45 days. If more time is needed, we will inform you
|
||||||
|
of the reason and extension period in writing. You may make a
|
||||||
|
request for your information twice every 12 months. If you are
|
||||||
|
making an erasure request, please include details of the
|
||||||
|
information you would like erased.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Please note that if you request that we remove your information,
|
||||||
|
we may retain some of the information for specific reasons, such
|
||||||
|
as to resolve disputes, troubleshoot problems, and as required by
|
||||||
|
law. Some information may not be completely removed from our
|
||||||
|
databases due to technical constraints and regular backups.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
We will not discriminate against you for exercising any of your
|
||||||
|
CCPA rights.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 class="text-white/80 text-base font-semibold">
|
||||||
|
10. Contact Us
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
If you have any questions or concerns about this Privacy Policy,
|
||||||
|
please contact us at{" "}
|
||||||
|
<a href="mailto:team@mintplexlabs.com">team@mintplexlabs.com</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 class="text-white/80 text-base font-semibold">
|
||||||
|
11. Changes to This Privacy Policy
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
We may update this Privacy Policy from time to time. We will
|
||||||
|
notify you of any changes by posting the new Privacy Policy on our
|
||||||
|
website. You are advised to review this Privacy Policy
|
||||||
|
periodically for any changes.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
By using our services, you agree to the terms of this Privacy
|
||||||
|
Policy.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleAccept}
|
||||||
|
type="button"
|
||||||
|
className="mt-8 w-full py-2 text-center text-white hover:bg-primary-button border-none rounded-lg"
|
||||||
|
>
|
||||||
|
Agree and continue →
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
188
frontend/src/pages/FineTuning/Steps/TermsAndConditions/index.jsx
Normal file
188
frontend/src/pages/FineTuning/Steps/TermsAndConditions/index.jsx
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
import FineTuningSteps from "..";
|
||||||
|
|
||||||
|
export default function TermsAndConditions({ setSettings, setStep }) {
|
||||||
|
const handleAccept = () => {
|
||||||
|
setSettings((prev) => {
|
||||||
|
return { ...prev, agreedToTerms: true };
|
||||||
|
});
|
||||||
|
setStep(FineTuningSteps.tos.next());
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
||||||
|
<div className="bg-[#303237] text-white rounded-xl flex-1 p-4">
|
||||||
|
<div className="w-full flex flex-col gap-y-4">
|
||||||
|
<h2 className="text-xl text-white font-semibold">
|
||||||
|
Terms and Conditions
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
Please accept the terms and conditions to continue with creation and
|
||||||
|
ordering of a fine-tune model.
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-col gap-y-2 text-white/75 text-sm border p-2 border-white rounded-lg font-mono h-[60vh] overflow-y-scroll">
|
||||||
|
<h1 className="text-white/80 text-lg font-semibold">
|
||||||
|
Mintplex Labs Inc. Fine-Tuning Terms of Service
|
||||||
|
</h1>
|
||||||
|
<p>
|
||||||
|
<strong>Last Updated:</strong> July 15, 2024
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This Agreement is between Mintplex Labs Inc. ("Company") and the
|
||||||
|
customer ("Customer") accessing or using the services provided by
|
||||||
|
the Company. By signing up, accessing, or using the services,
|
||||||
|
Customer indicates its acceptance of this Agreement and agrees to
|
||||||
|
be bound by the terms and conditions outlined below.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="text-white/80 text-base font-semibold">
|
||||||
|
1. Services Provided
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
Mintplex Labs Inc. provides model fine-tuning services for
|
||||||
|
customers. The deliverable for these services is a download link
|
||||||
|
to the output ".GGUF" file that can be used by the Customer for
|
||||||
|
Large-Language text inferencing.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="text-white/80 text-base font-semibold">
|
||||||
|
2. Payment Terms
|
||||||
|
</h2>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<strong>One-Time Payment:</strong> A one-time payment is
|
||||||
|
required before the execution of the training.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Payment Due Date:</strong> Payment is due upon order
|
||||||
|
placement.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Refund Policy:</strong> Payments are refundable in the
|
||||||
|
event of training failure or if the Company fails to deliver the
|
||||||
|
complete model file to the Customer.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2 className="text-white/80 text-base font-semibold">
|
||||||
|
3. Order Form
|
||||||
|
</h2>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<strong>Service:</strong> Model fine-tuning
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Payment Amount:</strong> As specified in the order form
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Payment Due Date:</strong> Upon order placement
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2 className="text-white/80 text-base font-semibold">
|
||||||
|
4. Customer Responsibilities
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
The Customer must provide all necessary data and information
|
||||||
|
required for model fine-tuning.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The Customer must ensure timely payment as per the terms mentioned
|
||||||
|
above.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The Customer understands the data collected for tuning will be
|
||||||
|
stored to a private cloud storage location temporarily while
|
||||||
|
training is in progress.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The Customer understands the data collected for tuning will be
|
||||||
|
fully deleted once the order is completed or canceled by the
|
||||||
|
Company.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The Customer understands and has reviewed the Privacy Policy for
|
||||||
|
Fine-Tuning by the Company.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="text-white/80 text-base font-semibold">
|
||||||
|
5. Refund Policy
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
Refunds will be processed in the event of training failure or if
|
||||||
|
the complete model file is not delivered to the Customer. Refunds
|
||||||
|
will be issued to the original payment method within 30 days of
|
||||||
|
the refund request.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="text-white/80 text-base font-semibold">
|
||||||
|
6. Governing Law
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
This Agreement shall be governed by and construed in accordance
|
||||||
|
with the laws of the State of California.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="text-white/80 text-base font-semibold">
|
||||||
|
7. Dispute Resolution
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
Any disputes arising out of or in connection with this Agreement
|
||||||
|
shall be resolved in the state or federal courts located in
|
||||||
|
California.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="text-white/80 text-base font-semibold">
|
||||||
|
8. Notices
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
All notices under this Agreement shall be in writing and shall be
|
||||||
|
deemed given when delivered personally, sent by confirmed email,
|
||||||
|
or sent by certified or registered mail, return receipt requested,
|
||||||
|
and addressed to the respective parties as follows:
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
For Company:{" "}
|
||||||
|
<a href="mailto:team@mintplexlabs.com">team@mintplexlabs.com</a>
|
||||||
|
</p>
|
||||||
|
<p>For Customer: The main email address on Customer's account</p>
|
||||||
|
|
||||||
|
<h2 className="text-white/80 text-base font-semibold">
|
||||||
|
9. Amendments
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
The Company reserves the right to amend these terms at any time by
|
||||||
|
providing notice to the Customer. The Customer's continued use of
|
||||||
|
the services after such amendments will constitute acceptance of
|
||||||
|
the amended terms.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="text-white/80 text-base font-semibold">
|
||||||
|
10. Indemnity
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
The Customer agrees to indemnify, defend, and hold harmless
|
||||||
|
Mintplex Labs Inc., its affiliates, and their respective officers,
|
||||||
|
directors, employees, agents, and representatives from and against
|
||||||
|
any and all claims, liabilities, damages, losses, costs, expenses,
|
||||||
|
fees (including reasonable attorneys' fees and court costs) that
|
||||||
|
arise from or relate to: (a) the Customer's use of the services;
|
||||||
|
(b) any violation of this Agreement by the Customer; (c) any
|
||||||
|
breach of any representation, warranty, or covenant made by the
|
||||||
|
Customer; or (d) the Customer's violation of any rights of another
|
||||||
|
person or entity.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleAccept}
|
||||||
|
type="button"
|
||||||
|
className="mt-8 w-full py-2 text-center text-white hover:bg-primary-button border-none rounded-lg"
|
||||||
|
>
|
||||||
|
Agree and continue →
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
148
frontend/src/pages/FineTuning/Steps/index.jsx
Normal file
148
frontend/src/pages/FineTuning/Steps/index.jsx
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
import { isMobile } from "react-device-detect";
|
||||||
|
import { useState } from "react";
|
||||||
|
import Sidebar from "@/components/Sidebar";
|
||||||
|
import Introduction from "./Introduction";
|
||||||
|
import PrivacyPolicy from "./Privacy";
|
||||||
|
import TermsAndConditions from "./TermsAndConditions";
|
||||||
|
import Fulfillment from "./FulfillmentPolicy";
|
||||||
|
import OrderDetails from "./OrderDetails";
|
||||||
|
import DataUpload from "./DataUpload";
|
||||||
|
import Confirmation from "./Confirmation";
|
||||||
|
import OrderPlaced from "./OrderPlaced";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef OrderSettings
|
||||||
|
* @property {string} email
|
||||||
|
* @property {string} baseModel
|
||||||
|
* @property {string} modelName
|
||||||
|
* @property {boolean} agreedToTerms
|
||||||
|
* @property {boolean} agreedToPrivacy
|
||||||
|
* @property {string} modelName
|
||||||
|
* @property {string|null} checkoutUrl
|
||||||
|
* @property {string|null} jobId
|
||||||
|
* @property {{slugs: string[], feedback: boolean|null}} trainingData
|
||||||
|
* @property {{pricing: {usd: number}, availableBaseModels: string[]}} tuningInfo
|
||||||
|
*/
|
||||||
|
|
||||||
|
const FineTuningSteps = {
|
||||||
|
intro: {
|
||||||
|
name: "Introduction to Fine-Tuning",
|
||||||
|
next: () => "privacy",
|
||||||
|
component: ({ settings, setSettings, setStep }) => (
|
||||||
|
<Introduction
|
||||||
|
settings={settings}
|
||||||
|
setSettings={setSettings}
|
||||||
|
setStep={setStep}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
privacy: {
|
||||||
|
name: "How your data is handled",
|
||||||
|
next: () => "tos",
|
||||||
|
component: ({ settings, setSettings, setStep }) => (
|
||||||
|
<PrivacyPolicy
|
||||||
|
settings={settings}
|
||||||
|
setSettings={setSettings}
|
||||||
|
setStep={setStep}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
tos: {
|
||||||
|
name: "Terms of service",
|
||||||
|
next: () => "fulfillment",
|
||||||
|
component: ({ settings, setSettings, setStep }) => (
|
||||||
|
<TermsAndConditions
|
||||||
|
settings={settings}
|
||||||
|
setSettings={setSettings}
|
||||||
|
setStep={setStep}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
fulfillment: {
|
||||||
|
name: "Fulfillment terms",
|
||||||
|
next: () => "order-details",
|
||||||
|
component: ({ settings, setSettings, setStep }) => (
|
||||||
|
<Fulfillment
|
||||||
|
settings={settings}
|
||||||
|
setSettings={setSettings}
|
||||||
|
setStep={setStep}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"order-details": {
|
||||||
|
name: "Model details & information",
|
||||||
|
next: () => "data-selection",
|
||||||
|
component: ({ settings, setSettings, setStep }) => (
|
||||||
|
<OrderDetails
|
||||||
|
settings={settings}
|
||||||
|
setSettings={setSettings}
|
||||||
|
setStep={setStep}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"data-selection": {
|
||||||
|
name: "Data selection",
|
||||||
|
next: () => "confirmation",
|
||||||
|
component: ({ settings, setSettings, setStep }) => (
|
||||||
|
<DataUpload
|
||||||
|
settings={settings}
|
||||||
|
setSettings={setSettings}
|
||||||
|
setStep={setStep}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
confirmation: {
|
||||||
|
name: "Review and Submit",
|
||||||
|
next: () => "done",
|
||||||
|
component: ({ settings, setSettings, setStep }) => (
|
||||||
|
<Confirmation
|
||||||
|
settings={settings}
|
||||||
|
setSettings={setSettings}
|
||||||
|
setStep={setStep}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
done: {
|
||||||
|
name: "Order placed",
|
||||||
|
next: () => "done",
|
||||||
|
component: ({ settings }) => <OrderPlaced settings={settings} />,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function FineTuningCreationLayout({ setStep, children }) {
|
||||||
|
const [settings, setSettings] = useState({
|
||||||
|
email: null,
|
||||||
|
baseModel: null,
|
||||||
|
modelName: null,
|
||||||
|
agreedToTerms: false,
|
||||||
|
agreedToPrivacy: false,
|
||||||
|
data: {
|
||||||
|
workspaceSlugs: [],
|
||||||
|
feedback: false,
|
||||||
|
},
|
||||||
|
tuningInfo: {
|
||||||
|
pricing: {
|
||||||
|
usd: 0.0,
|
||||||
|
},
|
||||||
|
availableBaseModels: [],
|
||||||
|
},
|
||||||
|
checkoutUrl: null,
|
||||||
|
jobId: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="fine-tune-create-order-container"
|
||||||
|
className="w-screen h-screen overflow-y-auto bg-sidebar flex"
|
||||||
|
>
|
||||||
|
<Sidebar />
|
||||||
|
<div
|
||||||
|
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
||||||
|
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] w-full h-full flex"
|
||||||
|
>
|
||||||
|
{children(settings, setSettings, setStep)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default FineTuningSteps;
|
70
frontend/src/pages/FineTuning/index.jsx
Normal file
70
frontend/src/pages/FineTuning/index.jsx
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import FineTuningSteps, { FineTuningCreationLayout } from "./Steps";
|
||||||
|
import { CheckCircle, Circle, Sparkle } from "@phosphor-icons/react";
|
||||||
|
import { isMobile } from "react-device-detect";
|
||||||
|
|
||||||
|
function SideBarSelection({ currentStep }) {
|
||||||
|
const currentIndex = Object.keys(FineTuningSteps).indexOf(currentStep);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`bg-white/5 text-white rounded-xl ${
|
||||||
|
isMobile ? "w-full" : "min-w-[360px] w-fit"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{Object.entries(FineTuningSteps).map(([stepKey, props], index) => {
|
||||||
|
const isSelected = currentStep === stepKey;
|
||||||
|
const isLast = index === Object.keys(FineTuningSteps).length - 1;
|
||||||
|
const isDone =
|
||||||
|
currentIndex === Object.keys(FineTuningSteps).length - 1 ||
|
||||||
|
index < currentIndex;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={stepKey}
|
||||||
|
className={[
|
||||||
|
"py-3 px-4 flex items-center justify-between transition-all duration-300",
|
||||||
|
isSelected ? "rounded-t-xl" : "",
|
||||||
|
isLast ? "" : "border-b border-white/10",
|
||||||
|
].join(" ")}
|
||||||
|
>
|
||||||
|
<div className="text-sm font-light">{props.name}</div>
|
||||||
|
<div className="flex items-center gap-x-2">
|
||||||
|
{isDone ? (
|
||||||
|
<CheckCircle className={`text-green-300`} />
|
||||||
|
) : (
|
||||||
|
<Circle
|
||||||
|
className={`text-white-800 ${
|
||||||
|
isSelected ? "animate-pulse" : "opacity-10"
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function FineTuningFlow() {
|
||||||
|
const [step, setStep] = useState("intro");
|
||||||
|
const StepPage = FineTuningSteps.hasOwnProperty(step)
|
||||||
|
? FineTuningSteps[step]
|
||||||
|
: FineTuningSteps.intro;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FineTuningCreationLayout setStep={setStep}>
|
||||||
|
{(settings, setSettings, setStep) => (
|
||||||
|
<div className="flex-1 flex gap-x-6 p-4 mt-10">
|
||||||
|
<div className="flex flex-col gap-y-[18px]">
|
||||||
|
<div className="text-white flex items-center gap-x-2">
|
||||||
|
<Sparkle size={24} />
|
||||||
|
<p className="text-lg font-medium">Custom Fine-Tuned Model</p>
|
||||||
|
</div>
|
||||||
|
<SideBarSelection currentStep={step} />
|
||||||
|
</div>
|
||||||
|
{StepPage.component({ settings, setSettings, setStep })}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</FineTuningCreationLayout>
|
||||||
|
);
|
||||||
|
}
|
@ -7,9 +7,10 @@ import useQuery from "@/hooks/useQuery";
|
|||||||
import ChatRow from "./ChatRow";
|
import ChatRow from "./ChatRow";
|
||||||
import showToast from "@/utils/toast";
|
import showToast from "@/utils/toast";
|
||||||
import System from "@/models/system";
|
import System from "@/models/system";
|
||||||
import { CaretDown, Download, Trash } from "@phosphor-icons/react";
|
import { CaretDown, Download, Sparkle, Trash } from "@phosphor-icons/react";
|
||||||
import { saveAs } from "file-saver";
|
import { saveAs } from "file-saver";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import paths from "@/utils/paths";
|
||||||
|
|
||||||
const exportOptions = {
|
const exportOptions = {
|
||||||
csv: {
|
csv: {
|
||||||
@ -159,13 +160,22 @@ export default function WorkspaceChats() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{chats.length > 0 && (
|
{chats.length > 0 && (
|
||||||
<button
|
<>
|
||||||
onClick={handleClearAllChats}
|
<button
|
||||||
className="flex items-center gap-x-2 px-4 py-1 border hover:border-transparent border-white/40 text-white/40 rounded-lg bg-transparent hover:text-white text-xs font-semibold hover:bg-red-500 shadow-[0_4px_14px_rgba(0,0,0,0.25)] h-[34px] w-fit"
|
onClick={handleClearAllChats}
|
||||||
>
|
className="flex items-center gap-x-2 px-4 py-1 border hover:border-transparent border-white/40 text-white/40 rounded-lg bg-transparent hover:text-white text-xs font-semibold hover:bg-red-500 shadow-[0_4px_14px_rgba(0,0,0,0.25)] h-[34px] w-fit"
|
||||||
<Trash size={18} weight="bold" />
|
>
|
||||||
Clear Chats
|
<Trash size={18} weight="bold" />
|
||||||
</button>
|
Clear Chats
|
||||||
|
</button>
|
||||||
|
<a
|
||||||
|
href={paths.orderFineTune()}
|
||||||
|
className="flex items-center gap-x-2 px-4 py-1 border hover:border-transparent border-yellow-300 text-yellow-300/80 rounded-lg bg-transparent hover:text-white text-xs font-semibold hover:bg-yellow-300/75 shadow-[0_4px_14px_rgba(0,0,0,0.25)] h-[34px] w-fit"
|
||||||
|
>
|
||||||
|
<Sparkle size={18} weight="bold" />
|
||||||
|
Order Fine-Tune Model
|
||||||
|
</a>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs leading-[18px] font-base text-white text-opacity-60">
|
<p className="text-xs leading-[18px] font-base text-white text-opacity-60">
|
||||||
|
@ -54,7 +54,7 @@ https://github.com/Mintplex-Labs/anything-llm/tree/master/embed/README.md
|
|||||||
data-base-api-url="${serverHost}/api/embed"
|
data-base-api-url="${serverHost}/api/embed"
|
||||||
src="${scriptHost}/embed/anythingllm-chat-widget.min.js">
|
src="${scriptHost}/embed/anythingllm-chat-widget.min.js">
|
||||||
</script>
|
</script>
|
||||||
<!-- AnythingLLM (https://useanything.com) -->
|
<!-- AnythingLLM (https://anythingllm.com) -->
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,7 +270,7 @@ export const PermittedDomains = ({ defaultValue = [] }) => {
|
|||||||
<TagsInput
|
<TagsInput
|
||||||
value={domains}
|
value={domains}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
placeholder="https://mysite.com, https://useanything.com"
|
placeholder="https://mysite.com, https://anythingllm.com"
|
||||||
classNames={{
|
classNames={{
|
||||||
tag: "bg-blue-300/10 text-zinc-800 m-1",
|
tag: "bg-blue-300/10 text-zinc-800 m-1",
|
||||||
input:
|
input:
|
||||||
|
@ -5,6 +5,7 @@ import PasswordModal, { usePasswordModal } from "@/components/Modals/Password";
|
|||||||
import { isMobile } from "react-device-detect";
|
import { isMobile } from "react-device-detect";
|
||||||
import { FullScreenLoader } from "@/components/Preloader";
|
import { FullScreenLoader } from "@/components/Preloader";
|
||||||
import UserMenu from "@/components/UserMenu";
|
import UserMenu from "@/components/UserMenu";
|
||||||
|
import { FineTuningAlert } from "../FineTuning/Banner";
|
||||||
|
|
||||||
export default function Main() {
|
export default function Main() {
|
||||||
const { loading, requiresAuth, mode } = usePasswordModal();
|
const { loading, requiresAuth, mode } = usePasswordModal();
|
||||||
@ -15,11 +16,14 @@ export default function Main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<UserMenu>
|
<>
|
||||||
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
<UserMenu>
|
||||||
{!isMobile && <Sidebar />}
|
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
||||||
<DefaultChatContainer />
|
{!isMobile && <Sidebar />}
|
||||||
</div>
|
<DefaultChatContainer />
|
||||||
</UserMenu>
|
</div>
|
||||||
|
</UserMenu>
|
||||||
|
<FineTuningAlert />
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import Workspace from "@/models/workspace";
|
|||||||
import PasswordModal, { usePasswordModal } from "@/components/Modals/Password";
|
import PasswordModal, { usePasswordModal } from "@/components/Modals/Password";
|
||||||
import { isMobile } from "react-device-detect";
|
import { isMobile } from "react-device-detect";
|
||||||
import { FullScreenLoader } from "@/components/Preloader";
|
import { FullScreenLoader } from "@/components/Preloader";
|
||||||
|
import { FineTuningAlert } from "../FineTuning/Banner";
|
||||||
|
|
||||||
export default function WorkspaceChat() {
|
export default function WorkspaceChat() {
|
||||||
const { loading, requiresAuth, mode } = usePasswordModal();
|
const { loading, requiresAuth, mode } = usePasswordModal();
|
||||||
@ -44,9 +45,12 @@ function ShowWorkspaceChat() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
<>
|
||||||
{!isMobile && <Sidebar />}
|
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
||||||
<WorkspaceChatContainer loading={loading} workspace={workspace} />
|
{!isMobile && <Sidebar />}
|
||||||
</div>
|
<WorkspaceChatContainer loading={loading} workspace={workspace} />
|
||||||
|
</div>
|
||||||
|
<FineTuningAlert />
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ export default {
|
|||||||
return "https://discord.com/invite/6UyHPeGZAC";
|
return "https://discord.com/invite/6UyHPeGZAC";
|
||||||
},
|
},
|
||||||
docs: () => {
|
docs: () => {
|
||||||
return "https://docs.useanything.com";
|
return "https://docs.anythingllm.com";
|
||||||
},
|
},
|
||||||
mailToMintplex: () => {
|
mailToMintplex: () => {
|
||||||
return "mailto:team@mintplexlabs.com";
|
return "mailto:team@mintplexlabs.com";
|
||||||
@ -76,6 +76,9 @@ export default {
|
|||||||
apiDocs: () => {
|
apiDocs: () => {
|
||||||
return `${API_BASE}/docs`;
|
return `${API_BASE}/docs`;
|
||||||
},
|
},
|
||||||
|
orderFineTune: () => {
|
||||||
|
return `/fine-tuning`;
|
||||||
|
},
|
||||||
settings: {
|
settings: {
|
||||||
system: () => {
|
system: () => {
|
||||||
return `/settings/system-preferences`;
|
return `/settings/system-preferences`;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<a name="readme-top"></a>
|
<a name="readme-top"></a>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://useanything.com"><img src="https://github.com/Mintplex-Labs/anything-llm/blob/master/images/wordmark.png?raw=true" alt="AnythingLLM logo"></a>
|
<a href="https://anythingllm.com"><img src="https://github.com/Mintplex-Labs/anything-llm/blob/master/images/wordmark.png?raw=true" alt="AnythingLLM logo"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div align='center'>
|
<div align='center'>
|
||||||
@ -20,7 +20,7 @@
|
|||||||
<a href="https://github.com/Mintplex-Labs/anything-llm/blob/master/LICENSE" target="_blank">
|
<a href="https://github.com/Mintplex-Labs/anything-llm/blob/master/LICENSE" target="_blank">
|
||||||
<img src="https://img.shields.io/static/v1?label=license&message=MIT&color=white" alt="ライセンス">
|
<img src="https://img.shields.io/static/v1?label=license&message=MIT&color=white" alt="ライセンス">
|
||||||
</a> |
|
</a> |
|
||||||
<a href="https://docs.useanything.com" target="_blank">
|
<a href="https://docs.anythingllm.com" target="_blank">
|
||||||
ドキュメント
|
ドキュメント
|
||||||
</a> |
|
</a> |
|
||||||
<a href="https://my.mintplexlabs.com/aio-checkout?product=anythingllm" target="_blank">
|
<a href="https://my.mintplexlabs.com/aio-checkout?product=anythingllm" target="_blank">
|
||||||
@ -33,7 +33,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
👉 デスクトップ用AnythingLLM(Mac、Windows、Linux対応)!<a href="https://useanything.com/download" target="_blank">今すぐダウンロード</a>
|
👉 デスクトップ用AnythingLLM(Mac、Windows、Linux対応)!<a href="https://anythingllm.com/download" target="_blank">今すぐダウンロード</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
これは、任意のドキュメント、リソース、またはコンテンツの断片を、チャット中にLLMが参照として使用できるコンテキストに変換できるフルスタックアプリケーションです。このアプリケーションを使用すると、使用するLLMまたはベクトルデータベースを選択し、マルチユーザー管理と権限をサポートできます。
|
これは、任意のドキュメント、リソース、またはコンテンツの断片を、チャット中にLLMが参照として使用できるコンテキストに変換できるフルスタックアプリケーションです。このアプリケーションを使用すると、使用するLLMまたはベクトルデータベースを選択し、マルチユーザー管理と権限をサポートできます。
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<a name="readme-top"></a>
|
<a name="readme-top"></a>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://useanything.com"><img src="https://github.com/Mintplex-Labs/anything-llm/blob/master/images/wordmark.png?raw=true" alt="AnythingLLM logo"></a>
|
<a href="https://anythingllm.com"><img src="https://github.com/Mintplex-Labs/anything-llm/blob/master/images/wordmark.png?raw=true" alt="AnythingLLM logo"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@ -16,7 +16,7 @@
|
|||||||
<a href="https://github.com/Mintplex-Labs/anything-llm/blob/master/LICENSE" target="_blank">
|
<a href="https://github.com/Mintplex-Labs/anything-llm/blob/master/LICENSE" target="_blank">
|
||||||
<img src="https://img.shields.io/static/v1?label=license&message=MIT&color=white" alt="许可证">
|
<img src="https://img.shields.io/static/v1?label=license&message=MIT&color=white" alt="许可证">
|
||||||
</a> |
|
</a> |
|
||||||
<a href="https://docs.useanything.com" target="_blank">
|
<a href="https://docs.anythingllm.com" target="_blank">
|
||||||
文档
|
文档
|
||||||
</a> |
|
</a> |
|
||||||
<a href="https://my.mintplexlabs.com/aio-checkout?product=anythingllm" target="_blank">
|
<a href="https://my.mintplexlabs.com/aio-checkout?product=anythingllm" target="_blank">
|
||||||
@ -25,11 +25,11 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href='../README.md'>English</a> · <b>简体中文</b> · <a href='./README.ja-JP.md'>简体中文</a>
|
<a href='../README.md'>English</a> · <b>简体中文</b> · <a href='./README.ja-JP.md'>日本語</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
👉 适用于桌面(Mac、Windows和Linux)的AnythingLLM!<a href="https://useanything.com/download" target="_blank">立即下载</a>
|
👉 适用于桌面(Mac、Windows和Linux)的AnythingLLM!<a href="https://anythingllm.com/download" target="_blank">立即下载</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
这是一个全栈应用程序,可以将任何文档、资源(如网址链接、音频、视频)或内容片段转换为上下文,以便任何大语言模型(LLM)在聊天期间作为参考使用。此应用程序允许您选择使用哪个LLM或向量数据库,同时支持多用户管理并设置不同权限。
|
这是一个全栈应用程序,可以将任何文档、资源(如网址链接、音频、视频)或内容片段转换为上下文,以便任何大语言模型(LLM)在聊天期间作为参考使用。此应用程序允许您选择使用哪个LLM或向量数据库,同时支持多用户管理并设置不同权限。
|
||||||
|
@ -137,7 +137,7 @@ function apiDocumentEndpoints(app) {
|
|||||||
schema: {
|
schema: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
example: {
|
example: {
|
||||||
"link": "https://useanything.com"
|
"link": "https://anythingllm.com"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,7 +159,7 @@ function apiDocumentEndpoints(app) {
|
|||||||
"docAuthor": "no author found",
|
"docAuthor": "no author found",
|
||||||
"description": "No description found.",
|
"description": "No description found.",
|
||||||
"docSource": "URL link uploaded by the user.",
|
"docSource": "URL link uploaded by the user.",
|
||||||
"chunkSource": "https:useanything.com.html",
|
"chunkSource": "https:anythingllm.com.html",
|
||||||
"published": "1/16/2024, 3:46:33 PM",
|
"published": "1/16/2024, 3:46:33 PM",
|
||||||
"wordCount": 252,
|
"wordCount": 252,
|
||||||
"pageContent": "AnythingLLM is the best....",
|
"pageContent": "AnythingLLM is the best....",
|
||||||
@ -237,6 +237,7 @@ function apiDocumentEndpoints(app) {
|
|||||||
example: {
|
example: {
|
||||||
"textContent": "This is the raw text that will be saved as a document in AnythingLLM.",
|
"textContent": "This is the raw text that will be saved as a document in AnythingLLM.",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
|
"title": "This key is required. See in /server/endpoints/api/document/index.js:287",
|
||||||
keyOne: "valueOne",
|
keyOne: "valueOne",
|
||||||
keyTwo: "valueTwo",
|
keyTwo: "valueTwo",
|
||||||
etc: "etc"
|
etc: "etc"
|
||||||
|
108
server/endpoints/experimental/fineTuning.js
Normal file
108
server/endpoints/experimental/fineTuning.js
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
const { FineTuning } = require("../../models/fineTuning");
|
||||||
|
const { Telemetry } = require("../../models/telemetry");
|
||||||
|
const { WorkspaceChats } = require("../../models/workspaceChats");
|
||||||
|
const { reqBody } = require("../../utils/http");
|
||||||
|
const {
|
||||||
|
flexUserRoleValid,
|
||||||
|
ROLES,
|
||||||
|
} = require("../../utils/middleware/multiUserProtected");
|
||||||
|
const { validatedRequest } = require("../../utils/middleware/validatedRequest");
|
||||||
|
|
||||||
|
function fineTuningEndpoints(app) {
|
||||||
|
if (!app) return;
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
"/experimental/fine-tuning/check-eligible",
|
||||||
|
[validatedRequest, flexUserRoleValid([ROLES.admin])],
|
||||||
|
async (_request, response) => {
|
||||||
|
try {
|
||||||
|
const chatCount = await WorkspaceChats.count();
|
||||||
|
response
|
||||||
|
.status(200)
|
||||||
|
.json({ eligible: chatCount >= FineTuning.recommendedMinDataset });
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
response.status(500).end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
"/experimental/fine-tuning/info",
|
||||||
|
[validatedRequest, flexUserRoleValid([ROLES.admin])],
|
||||||
|
async (_request, response) => {
|
||||||
|
try {
|
||||||
|
const fineTuningInfo = await FineTuning.getInfo();
|
||||||
|
await Telemetry.sendTelemetry("fine_tuning_interest", {
|
||||||
|
step: "information",
|
||||||
|
});
|
||||||
|
response.status(200).json(fineTuningInfo);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
response.status(500).end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
app.post(
|
||||||
|
"/experimental/fine-tuning/dataset",
|
||||||
|
[validatedRequest, flexUserRoleValid([ROLES.admin])],
|
||||||
|
async (request, response) => {
|
||||||
|
try {
|
||||||
|
const { slugs = [], feedback = null } = reqBody(request);
|
||||||
|
if (!Array.isArray(slugs) || slugs.length === 0) {
|
||||||
|
return response.status(200).json({
|
||||||
|
count: 0,
|
||||||
|
recommendedMin: FineTuning.recommendedMinDataset,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const count = await FineTuning.datasetSize(slugs, feedback);
|
||||||
|
await Telemetry.sendTelemetry("fine_tuning_interest", {
|
||||||
|
step: "uploaded_dataset",
|
||||||
|
});
|
||||||
|
response
|
||||||
|
.status(200)
|
||||||
|
.json({ count, recommendedMin: FineTuning.recommendedMinDataset });
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
response.status(500).end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
app.post(
|
||||||
|
"/experimental/fine-tuning/order",
|
||||||
|
[validatedRequest, flexUserRoleValid([ROLES.admin])],
|
||||||
|
async (request, response) => {
|
||||||
|
try {
|
||||||
|
const { email, baseModel, modelName, trainingData } = reqBody(request);
|
||||||
|
if (
|
||||||
|
!email ||
|
||||||
|
!baseModel ||
|
||||||
|
!modelName ||
|
||||||
|
!trainingData ||
|
||||||
|
!trainingData?.slugs.length
|
||||||
|
)
|
||||||
|
throw new Error("Invalid order details");
|
||||||
|
|
||||||
|
const { jobId, checkoutUrl } = await FineTuning.newOrder({
|
||||||
|
email,
|
||||||
|
baseModel,
|
||||||
|
modelName,
|
||||||
|
trainingData,
|
||||||
|
});
|
||||||
|
await Telemetry.sendTelemetry("fine_tuning_interest", {
|
||||||
|
step: "created_order",
|
||||||
|
jobId,
|
||||||
|
});
|
||||||
|
response.status(200).json({ jobId, checkoutUrl });
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
response.status(500).end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { fineTuningEndpoints };
|
@ -1,3 +1,4 @@
|
|||||||
|
const { fineTuningEndpoints } = require("./fineTuning");
|
||||||
const { liveSyncEndpoints } = require("./liveSync");
|
const { liveSyncEndpoints } = require("./liveSync");
|
||||||
|
|
||||||
// All endpoints here are not stable and can move around - have breaking changes
|
// All endpoints here are not stable and can move around - have breaking changes
|
||||||
@ -5,6 +6,7 @@ const { liveSyncEndpoints } = require("./liveSync");
|
|||||||
// When a feature is promoted it should be removed from here and added to the appropriate scope.
|
// When a feature is promoted it should be removed from here and added to the appropriate scope.
|
||||||
function experimentalEndpoints(router) {
|
function experimentalEndpoints(router) {
|
||||||
liveSyncEndpoints(router);
|
liveSyncEndpoints(router);
|
||||||
|
fineTuningEndpoints(router);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { experimentalEndpoints };
|
module.exports = { experimentalEndpoints };
|
||||||
|
222
server/models/fineTuning.js
Normal file
222
server/models/fineTuning.js
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
const { default: slugify } = require("slugify");
|
||||||
|
const { safeJsonParse } = require("../utils/http");
|
||||||
|
const { Telemetry } = require("./telemetry");
|
||||||
|
const { Workspace } = require("./workspace");
|
||||||
|
const { WorkspaceChats } = require("./workspaceChats");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const { v4: uuidv4 } = require("uuid");
|
||||||
|
const tmpStorage =
|
||||||
|
process.env.NODE_ENV === "development"
|
||||||
|
? path.resolve(__dirname, `../storage/tmp`)
|
||||||
|
: path.resolve(
|
||||||
|
process.env.STORAGE_DIR ?? path.resolve(__dirname, `../storage`),
|
||||||
|
`tmp`
|
||||||
|
);
|
||||||
|
|
||||||
|
const FineTuning = {
|
||||||
|
API_BASE:
|
||||||
|
process.env.NODE_ENV === "development"
|
||||||
|
? process.env.FINE_TUNING_ORDER_API
|
||||||
|
: "https://finetuning-wxich7363q-uc.a.run.app",
|
||||||
|
recommendedMinDataset: 50,
|
||||||
|
standardPrompt:
|
||||||
|
"Given the following conversation, relevant context, and a follow up question, reply with an answer to the current question the user is asking. Return only your response to the question given the above information following the users instructions as needed.",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the information for the Fine-tuning product to display in various frontends
|
||||||
|
* @returns {Promise<{
|
||||||
|
* productDetails: {
|
||||||
|
* name: string,
|
||||||
|
* description: string,
|
||||||
|
* icon: string,
|
||||||
|
* active: boolean,
|
||||||
|
* },
|
||||||
|
* pricing: {
|
||||||
|
* usd: number,
|
||||||
|
* },
|
||||||
|
* availableBaseModels: string[]
|
||||||
|
* }>}
|
||||||
|
*/
|
||||||
|
getInfo: async function () {
|
||||||
|
return fetch(`${this.API_BASE}/info`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
Accepts: "application/json",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (!res.ok)
|
||||||
|
throw new Error("Could not fetch fine-tuning information endpoint");
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Get the Dataset size for a training set.
|
||||||
|
* @param {string[]} workspaceSlugs
|
||||||
|
* @param {boolean|null} feedback
|
||||||
|
* @returns {Promise<number>}
|
||||||
|
*/
|
||||||
|
datasetSize: async function (workspaceSlugs = [], feedback = null) {
|
||||||
|
const workspaceIds = await Workspace.where({
|
||||||
|
slug: {
|
||||||
|
in: workspaceSlugs.map((slug) => String(slug)),
|
||||||
|
},
|
||||||
|
}).then((results) => results.map((res) => res.id));
|
||||||
|
|
||||||
|
const count = await WorkspaceChats.count({
|
||||||
|
workspaceId: {
|
||||||
|
in: workspaceIds,
|
||||||
|
},
|
||||||
|
...(feedback === true ? { feedback: 1 } : {}),
|
||||||
|
});
|
||||||
|
return count;
|
||||||
|
},
|
||||||
|
|
||||||
|
_writeToTempStorage: function (data) {
|
||||||
|
const tmpFilepath = path.resolve(tmpStorage, `${uuidv4()}.json`);
|
||||||
|
if (!fs.existsSync(tmpStorage))
|
||||||
|
fs.mkdirSync(tmpStorage, { recursive: true });
|
||||||
|
fs.writeFileSync(tmpFilepath, JSON.stringify(data, null, 4));
|
||||||
|
return tmpFilepath;
|
||||||
|
},
|
||||||
|
|
||||||
|
_rmTempDatafile: function (datafileLocation) {
|
||||||
|
if (!datafileLocation || !fs.existsSync(datafileLocation)) return;
|
||||||
|
fs.rmSync(datafileLocation);
|
||||||
|
},
|
||||||
|
|
||||||
|
_uploadDatafile: async function (datafileLocation, uploadConfig) {
|
||||||
|
try {
|
||||||
|
const fileBuffer = fs.readFileSync(datafileLocation);
|
||||||
|
const formData = new FormData();
|
||||||
|
Object.entries(uploadConfig.fields).forEach(([key, value]) =>
|
||||||
|
formData.append(key, value)
|
||||||
|
);
|
||||||
|
formData.append("file", fileBuffer);
|
||||||
|
const response = await fetch(uploadConfig.url, {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("File upload returned code:", response.status);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error uploading file:", error.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_buildSystemPrompt: function (chat, prompt = null) {
|
||||||
|
const sources = safeJsonParse(chat.response)?.sources || [];
|
||||||
|
const contextTexts = sources.map((source) => source.text);
|
||||||
|
const context =
|
||||||
|
sources.length > 0
|
||||||
|
? "\nContext:\n" +
|
||||||
|
contextTexts
|
||||||
|
.map((text, i) => {
|
||||||
|
return `[CONTEXT ${i}]:\n${text}\n[END CONTEXT ${i}]\n\n`;
|
||||||
|
})
|
||||||
|
.join("")
|
||||||
|
: "";
|
||||||
|
return `${prompt ?? this.standardPrompt}${context}`;
|
||||||
|
},
|
||||||
|
|
||||||
|
_createTempDataFile: async function ({ slugs, feedback }) {
|
||||||
|
const workspacePromptMap = {};
|
||||||
|
const workspaces = await Workspace.where({
|
||||||
|
slug: {
|
||||||
|
in: slugs.map((slug) => String(slug)),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
workspaces.forEach((ws) => {
|
||||||
|
workspacePromptMap[ws.id] = ws.openAiPrompt ?? this.standardPrompt;
|
||||||
|
});
|
||||||
|
|
||||||
|
const chats = await WorkspaceChats.whereWithData({
|
||||||
|
workspaceId: {
|
||||||
|
in: workspaces.map((ws) => ws.id),
|
||||||
|
},
|
||||||
|
...(feedback === true ? { feedback: 1 } : {}),
|
||||||
|
});
|
||||||
|
const preparedData = chats.map((chat) => {
|
||||||
|
const responseJson = safeJsonParse(chat.response);
|
||||||
|
return {
|
||||||
|
instruction: this._buildSystemPrompt(
|
||||||
|
chat,
|
||||||
|
workspacePromptMap[chat.workspaceId]
|
||||||
|
),
|
||||||
|
input: chat.prompt,
|
||||||
|
output: responseJson.text,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const tmpFile = this._writeToTempStorage(preparedData);
|
||||||
|
return tmpFile;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate fine-tune order request
|
||||||
|
* @param {object} data
|
||||||
|
* @returns {Promise<{jobId:string, uploadParams: object, configReady: boolean, checkoutUrl:string}>}
|
||||||
|
*/
|
||||||
|
_requestOrder: async function (data = {}) {
|
||||||
|
return await fetch(`${this.API_BASE}/order/new`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Accepts: "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (!res.ok) throw new Error("Could not create fine-tune order");
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
return {
|
||||||
|
jobId: null,
|
||||||
|
uploadParams: null,
|
||||||
|
configReady: null,
|
||||||
|
checkoutUrl: null,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitizes the slugifies the model name to prevent issues during processing.
|
||||||
|
* only a-zA-Z0-9 are okay for model names. If name is totally invalid it becomes a uuid.
|
||||||
|
* @param {string} modelName - provided model name
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
_cleanModelName: function (modelName = "") {
|
||||||
|
if (!modelName) return uuidv4();
|
||||||
|
const sanitizedName = modelName.replace(/[^a-zA-Z0-9]/g, " ");
|
||||||
|
return slugify(sanitizedName);
|
||||||
|
},
|
||||||
|
|
||||||
|
newOrder: async function ({ email, baseModel, modelName, trainingData }) {
|
||||||
|
const datafileLocation = await this._createTempDataFile(trainingData);
|
||||||
|
const order = await this._requestOrder({
|
||||||
|
email,
|
||||||
|
baseModel,
|
||||||
|
modelName: this._cleanModelName(modelName),
|
||||||
|
orderExtras: { platform: Telemetry.runtime() },
|
||||||
|
});
|
||||||
|
const uploadComplete = await this._uploadDatafile(
|
||||||
|
datafileLocation,
|
||||||
|
order.uploadParams
|
||||||
|
);
|
||||||
|
if (!uploadComplete)
|
||||||
|
throw new Error("Data file upload failed. Order could not be created.");
|
||||||
|
this._rmTempDatafile(datafileLocation);
|
||||||
|
return { jobId: order.jobId, checkoutUrl: order.checkoutUrl };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = { FineTuning };
|
@ -416,6 +416,7 @@ const SystemSettings = {
|
|||||||
OllamaLLMBasePath: process.env.OLLAMA_BASE_PATH,
|
OllamaLLMBasePath: process.env.OLLAMA_BASE_PATH,
|
||||||
OllamaLLMModelPref: process.env.OLLAMA_MODEL_PREF,
|
OllamaLLMModelPref: process.env.OLLAMA_MODEL_PREF,
|
||||||
OllamaLLMTokenLimit: process.env.OLLAMA_MODEL_TOKEN_LIMIT,
|
OllamaLLMTokenLimit: process.env.OLLAMA_MODEL_TOKEN_LIMIT,
|
||||||
|
OllamaLLMKeepAliveSeconds: process.env.OLLAMA_KEEP_ALIVE_TIMEOUT ?? 300,
|
||||||
|
|
||||||
// TogetherAI Keys
|
// TogetherAI Keys
|
||||||
TogetherAiApiKey: !!process.env.TOGETHER_AI_API_KEY,
|
TogetherAiApiKey: !!process.env.TOGETHER_AI_API_KEY,
|
||||||
|
@ -903,7 +903,7 @@
|
|||||||
"docAuthor": "no author found",
|
"docAuthor": "no author found",
|
||||||
"description": "No description found.",
|
"description": "No description found.",
|
||||||
"docSource": "URL link uploaded by the user.",
|
"docSource": "URL link uploaded by the user.",
|
||||||
"chunkSource": "https:useanything.com.html",
|
"chunkSource": "https:anythingllm.com.html",
|
||||||
"published": "1/16/2024, 3:46:33 PM",
|
"published": "1/16/2024, 3:46:33 PM",
|
||||||
"wordCount": 252,
|
"wordCount": 252,
|
||||||
"pageContent": "AnythingLLM is the best....",
|
"pageContent": "AnythingLLM is the best....",
|
||||||
@ -944,7 +944,7 @@
|
|||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"example": {
|
"example": {
|
||||||
"link": "https://useanything.com"
|
"link": "https://anythingllm.com"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1023,6 +1023,7 @@
|
|||||||
"example": {
|
"example": {
|
||||||
"textContent": "This is the raw text that will be saved as a document in AnythingLLM.",
|
"textContent": "This is the raw text that will be saved as a document in AnythingLLM.",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
|
"title": "This key is required. See in /server/endpoints/api/document/index.js:287",
|
||||||
"keyOne": "valueOne",
|
"keyOne": "valueOne",
|
||||||
"keyTwo": "valueTwo",
|
"keyTwo": "valueTwo",
|
||||||
"etc": "etc"
|
"etc": "etc"
|
||||||
|
@ -13,6 +13,9 @@ class OllamaAILLM {
|
|||||||
|
|
||||||
this.basePath = process.env.OLLAMA_BASE_PATH;
|
this.basePath = process.env.OLLAMA_BASE_PATH;
|
||||||
this.model = modelPreference || process.env.OLLAMA_MODEL_PREF;
|
this.model = modelPreference || process.env.OLLAMA_MODEL_PREF;
|
||||||
|
this.keepAlive = process.env.OLLAMA_KEEP_ALIVE_TIMEOUT
|
||||||
|
? Number(process.env.OLLAMA_KEEP_ALIVE_TIMEOUT)
|
||||||
|
: 300; // Default 5-minute timeout for Ollama model loading.
|
||||||
this.limits = {
|
this.limits = {
|
||||||
history: this.promptWindowLimit() * 0.15,
|
history: this.promptWindowLimit() * 0.15,
|
||||||
system: this.promptWindowLimit() * 0.15,
|
system: this.promptWindowLimit() * 0.15,
|
||||||
@ -28,6 +31,7 @@ class OllamaAILLM {
|
|||||||
return new ChatOllama({
|
return new ChatOllama({
|
||||||
baseUrl: this.basePath,
|
baseUrl: this.basePath,
|
||||||
model: this.model,
|
model: this.model,
|
||||||
|
keepAlive: this.keepAlive,
|
||||||
useMLock: true,
|
useMLock: true,
|
||||||
temperature,
|
temperature,
|
||||||
});
|
});
|
||||||
|
@ -24,7 +24,7 @@ class OpenRouterLLM {
|
|||||||
baseURL: this.basePath,
|
baseURL: this.basePath,
|
||||||
apiKey: process.env.OPENROUTER_API_KEY ?? null,
|
apiKey: process.env.OPENROUTER_API_KEY ?? null,
|
||||||
defaultHeaders: {
|
defaultHeaders: {
|
||||||
"HTTP-Referer": "https://useanything.com",
|
"HTTP-Referer": "https://anythingllm.com",
|
||||||
"X-Title": "AnythingLLM",
|
"X-Title": "AnythingLLM",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -19,8 +19,8 @@ const webScraping = {
|
|||||||
"Scrapes the content of a webpage or online resource from a provided URL.",
|
"Scrapes the content of a webpage or online resource from a provided URL.",
|
||||||
examples: [
|
examples: [
|
||||||
{
|
{
|
||||||
prompt: "What is useanything.com about?",
|
prompt: "What is anythingllm.com about?",
|
||||||
call: JSON.stringify({ url: "https://useanything.com" }),
|
call: JSON.stringify({ url: "https://anythingllm.com" }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prompt: "Scrape https://example.com",
|
prompt: "Scrape https://example.com",
|
||||||
|
@ -78,7 +78,7 @@ class Provider {
|
|||||||
configuration: {
|
configuration: {
|
||||||
baseURL: "https://openrouter.ai/api/v1",
|
baseURL: "https://openrouter.ai/api/v1",
|
||||||
defaultHeaders: {
|
defaultHeaders: {
|
||||||
"HTTP-Referer": "https://useanything.com",
|
"HTTP-Referer": "https://anythingllm.com",
|
||||||
"X-Title": "AnythingLLM",
|
"X-Title": "AnythingLLM",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -17,7 +17,7 @@ class OpenRouterProvider extends InheritMultiple([Provider, UnTooled]) {
|
|||||||
apiKey: process.env.OPENROUTER_API_KEY,
|
apiKey: process.env.OPENROUTER_API_KEY,
|
||||||
maxRetries: 3,
|
maxRetries: 3,
|
||||||
defaultHeaders: {
|
defaultHeaders: {
|
||||||
"HTTP-Referer": "https://useanything.com",
|
"HTTP-Referer": "https://anythingllm.com",
|
||||||
"X-Title": "AnythingLLM",
|
"X-Title": "AnythingLLM",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -67,7 +67,7 @@ class MetaGenerator {
|
|||||||
{ tag: "meta", props: { property: "og:type", content: "website" } },
|
{ tag: "meta", props: { property: "og:type", content: "website" } },
|
||||||
{
|
{
|
||||||
tag: "meta",
|
tag: "meta",
|
||||||
props: { property: "og:url", content: "https://useanything.com" },
|
props: { property: "og:url", content: "https://anythingllm.com" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tag: "meta",
|
tag: "meta",
|
||||||
@ -99,7 +99,7 @@ class MetaGenerator {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
tag: "meta",
|
tag: "meta",
|
||||||
props: { property: "twitter:url", content: "https://useanything.com" },
|
props: { property: "twitter:url", content: "https://anythingllm.com" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tag: "meta",
|
tag: "meta",
|
||||||
|
@ -19,8 +19,13 @@ class TokenManager {
|
|||||||
// https://github.com/openai/tiktoken/blob/9e79899bc248d5313c7dd73562b5e211d728723d/tiktoken/core.py#L91C20-L91C38
|
// https://github.com/openai/tiktoken/blob/9e79899bc248d5313c7dd73562b5e211d728723d/tiktoken/core.py#L91C20-L91C38
|
||||||
// Returns number[]
|
// Returns number[]
|
||||||
tokensFromString(input = "") {
|
tokensFromString(input = "") {
|
||||||
const tokens = this.encoder.encode(input, undefined, []);
|
try {
|
||||||
return tokens;
|
const tokens = this.encoder.encode(String(input), undefined, []);
|
||||||
|
return tokens;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bytesFromTokens(tokens = []) {
|
bytesFromTokens(tokens = []) {
|
||||||
|
@ -101,6 +101,10 @@ const KEY_MAPPING = {
|
|||||||
envKey: "OLLAMA_MODEL_TOKEN_LIMIT",
|
envKey: "OLLAMA_MODEL_TOKEN_LIMIT",
|
||||||
checks: [nonZero],
|
checks: [nonZero],
|
||||||
},
|
},
|
||||||
|
OllamaLLMKeepAliveSeconds: {
|
||||||
|
envKey: "OLLAMA_KEEP_ALIVE_TIMEOUT",
|
||||||
|
checks: [isInteger],
|
||||||
|
},
|
||||||
|
|
||||||
// Mistral AI API Settings
|
// Mistral AI API Settings
|
||||||
MistralApiKey: {
|
MistralApiKey: {
|
||||||
@ -454,6 +458,11 @@ function nonZero(input = "") {
|
|||||||
return Number(input) <= 0 ? "Value must be greater than zero" : null;
|
return Number(input) <= 0 ? "Value must be greater than zero" : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isInteger(input = "") {
|
||||||
|
if (isNaN(Number(input))) return "Value must be a number";
|
||||||
|
return Number(input);
|
||||||
|
}
|
||||||
|
|
||||||
function isValidURL(input = "") {
|
function isValidURL(input = "") {
|
||||||
try {
|
try {
|
||||||
new URL(input);
|
new URL(input);
|
||||||
|
Loading…
Reference in New Issue
Block a user