From f4cd7e17bce3989edd9246ca4736cc2ab2a307f3 Mon Sep 17 00:00:00 2001 From: Ali Salimli <67149699+elisalimli@users.noreply.github.com> Date: Sat, 13 Apr 2024 07:40:04 +0400 Subject: [PATCH 1/8] Amazon Bedrock Support (#955) * deps: add boto3 * deps: upgrade litellm from 1.29.4 to 13.5.2 * feat(backend): add amazon bedrock * feat(ui): add amazon bedrock * Update version --------- Co-authored-by: Ismail Pelaseyed --- libs/superagent/app/agents/llm.py | 3 + .../app/api/workflow_configs/saml_schema.py | 2 + libs/superagent/app/main.py | 2 +- libs/superagent/poetry.lock | 83 +++++++++++++++++-- .../migration.sql | 2 + libs/superagent/prisma/schema.prisma | 1 + libs/superagent/pyproject.toml | 5 +- libs/ui/app/integrations/llm.tsx | 13 ++- libs/ui/config/site.ts | 37 +++++++-- 9 files changed, 129 insertions(+), 19 deletions(-) create mode 100644 libs/superagent/prisma/migrations/20240412075016_add_aws_bedrock/migration.sql diff --git a/libs/superagent/app/agents/llm.py b/libs/superagent/app/agents/llm.py index f5a85f9eb..fcb19ce16 100644 --- a/libs/superagent/app/agents/llm.py +++ b/libs/superagent/app/agents/llm.py @@ -84,6 +84,9 @@ def get_llm_params(self): return { "temperature": options.get("temperature"), "max_tokens": options.get("max_tokens"), + "aws_access_key_id": options.get("aws_access_key_id"), + "aws_secret_access_key": options.get("aws_secret_access_key"), + "aws_region_name": options.get("aws_region_name"), } async def _get_prompt(self): diff --git a/libs/superagent/app/api/workflow_configs/saml_schema.py b/libs/superagent/app/api/workflow_configs/saml_schema.py index 11b34d33d..837da1242 100644 --- a/libs/superagent/app/api/workflow_configs/saml_schema.py +++ b/libs/superagent/app/api/workflow_configs/saml_schema.py @@ -148,6 +148,7 @@ class LLMAgentTool(BaseAgentToolModel, LLMAgent): LLMProvider.PERPLEXITY.value, LLMProvider.TOGETHER_AI.value, LLMProvider.ANTHROPIC.value, + LLMProvider.BEDROCK.value, ] @@ -157,6 +158,7 @@ class Workflow(BaseModel): # ~~OSS LLM providers~~ perplexity: Optional[LLMAgent] together_ai: Optional[LLMAgent] + bedrock: Optional[LLMAgent] anthropic: Optional[LLMAgent] llm: Optional[LLMAgent] = Field( description="Deprecated! Use LLM providers instead. e.g. `perplexity` or `together_ai`" diff --git a/libs/superagent/app/main.py b/libs/superagent/app/main.py index 346632b0a..bd5bfd13c 100644 --- a/libs/superagent/app/main.py +++ b/libs/superagent/app/main.py @@ -36,7 +36,7 @@ title="Superagent", docs_url="/", description="🥷 Run AI-agents with an API", - version="0.2.27", + version="0.2.29", servers=[{"url": config("SUPERAGENT_API_URL")}], ) diff --git a/libs/superagent/poetry.lock b/libs/superagent/poetry.lock index 80b409862..a5f2389a3 100644 --- a/libs/superagent/poetry.lock +++ b/libs/superagent/poetry.lock @@ -539,6 +539,47 @@ d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] +[[package]] +name = "boto3" +version = "1.34.83" +description = "The AWS SDK for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "boto3-1.34.83-py3-none-any.whl", hash = "sha256:33cf93f6de5176f1188c923f4de1ae149ed723b89ed12e434f2b2f628491769e"}, + {file = "boto3-1.34.83.tar.gz", hash = "sha256:9733ce811bd82feab506ad9309e375a79cabe8c6149061971c17754ce8997551"}, +] + +[package.dependencies] +botocore = ">=1.34.83,<1.35.0" +jmespath = ">=0.7.1,<2.0.0" +s3transfer = ">=0.10.0,<0.11.0" + +[package.extras] +crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] + +[[package]] +name = "botocore" +version = "1.34.83" +description = "Low-level, data-driven core of boto 3." +optional = false +python-versions = ">=3.8" +files = [ + {file = "botocore-1.34.83-py3-none-any.whl", hash = "sha256:0a3fbbe018416aeefa8978454fb0b8129adbaf556647b72269bf02e4bf1f4161"}, + {file = "botocore-1.34.83.tar.gz", hash = "sha256:0f302aa76283d4df62b4fbb6d3d20115c1a8957fc02171257fc93904d69d5636"}, +] + +[package.dependencies] +jmespath = ">=0.7.1,<2.0.0" +python-dateutil = ">=2.1,<3.0.0" +urllib3 = [ + {version = ">=1.25.4,<1.27", markers = "python_version < \"3.10\""}, + {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""}, +] + +[package.extras] +crt = ["awscrt (==0.19.19)"] + [[package]] name = "bs4" version = "0.0.1" @@ -1894,6 +1935,17 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "jmespath" +version = "1.0.1" +description = "JSON Matching Expressions" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, + {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, +] + [[package]] name = "joblib" version = "1.3.2" @@ -2259,13 +2311,13 @@ requests = ">=2,<3" [[package]] name = "litellm" -version = "1.29.4" +version = "1.35.2" description = "Library to easily interface with LLM API providers" optional = false -python-versions = ">=3.8, !=2.7.*, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*, !=3.7.*" +python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" files = [ - {file = "litellm-1.29.4-py3-none-any.whl", hash = "sha256:014b03fd37864d12acb095511f42bb46b74bf77a0c7086eb5d7d3ea0a27cc238"}, - {file = "litellm-1.29.4.tar.gz", hash = "sha256:14a3e5c5aaa042b2a732374f56260afd7761625d8ee6ac38f6e1de1c5ee5f792"}, + {file = "litellm-1.35.2-py3-none-any.whl", hash = "sha256:686ee040154d7062b0078d882fa6399c5c7cc5ec9b5266490dee68f1b8905a36"}, + {file = "litellm-1.35.2.tar.gz", hash = "sha256:062e5be75196da7348ae0c4f60d396f0b23ee874708ed81c40f7675161213385"}, ] [package.dependencies] @@ -2280,8 +2332,8 @@ tiktoken = ">=0.4.0" tokenizers = "*" [package.extras] -extra-proxy = ["streamlit (>=1.29.0,<2.0.0)"] -proxy = ["PyJWT (>=2.8.0,<3.0.0)", "apscheduler (>=3.10.4,<4.0.0)", "backoff", "fastapi (>=0.104.1,<0.105.0)", "fastapi-sso (>=0.10.0,<0.11.0)", "gunicorn (>=21.2.0,<22.0.0)", "orjson (>=3.9.7,<4.0.0)", "python-multipart (>=0.0.6,<0.0.7)", "pyyaml (>=6.0.1,<7.0.0)", "rq", "uvicorn (>=0.22.0,<0.23.0)"] +extra-proxy = ["azure-identity (>=1.15.0,<2.0.0)", "azure-keyvault-secrets (>=4.8.0,<5.0.0)", "google-cloud-kms (>=2.21.3,<3.0.0)", "prisma (==0.11.0)", "resend (>=0.8.0,<0.9.0)"] +proxy = ["PyJWT (>=2.8.0,<3.0.0)", "apscheduler (>=3.10.4,<4.0.0)", "backoff", "cryptography (>=42.0.5,<43.0.0)", "fastapi (>=0.109.1,<0.110.0)", "fastapi-sso (>=0.10.0,<0.11.0)", "gunicorn (>=21.2.0,<22.0.0)", "orjson (>=3.9.7,<4.0.0)", "python-multipart (>=0.0.9,<0.0.10)", "pyyaml (>=6.0.1,<7.0.0)", "rq", "uvicorn (>=0.22.0,<0.23.0)"] [[package]] name = "llama-index" @@ -4467,6 +4519,23 @@ files = [ {file = "ruff-0.0.265.tar.gz", hash = "sha256:53c17f0dab19ddc22b254b087d1381b601b155acfa8feed514f0d6a413d0ab3a"}, ] +[[package]] +name = "s3transfer" +version = "0.10.1" +description = "An Amazon S3 Transfer Manager" +optional = false +python-versions = ">= 3.8" +files = [ + {file = "s3transfer-0.10.1-py3-none-any.whl", hash = "sha256:ceb252b11bcf87080fb7850a224fb6e05c8a776bab8f2b64b7f25b969464839d"}, + {file = "s3transfer-0.10.1.tar.gz", hash = "sha256:5683916b4c724f799e600f41dd9e10a9ff19871bf87623cc8f491cb4f5fa0a19"}, +] + +[package.dependencies] +botocore = ">=1.33.2,<2.0a.0" + +[package.extras] +crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] + [[package]] name = "scikit-learn" version = "1.3.2" @@ -5956,4 +6025,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.8.1, <3.12" -content-hash = "7beee27d48f14e756545373efd0e6ee914d8a8291200a417f3e7d8f826765852" +content-hash = "c390f22730e24482e7f42cc8140d339a1025fb1e25021c6d886cd4dafab3a622" diff --git a/libs/superagent/prisma/migrations/20240412075016_add_aws_bedrock/migration.sql b/libs/superagent/prisma/migrations/20240412075016_add_aws_bedrock/migration.sql new file mode 100644 index 000000000..81ae92e68 --- /dev/null +++ b/libs/superagent/prisma/migrations/20240412075016_add_aws_bedrock/migration.sql @@ -0,0 +1,2 @@ +-- AlterEnum +ALTER TYPE "LLMProvider" ADD VALUE 'BEDROCK'; diff --git a/libs/superagent/prisma/schema.prisma b/libs/superagent/prisma/schema.prisma index b198ce2cd..6f03bca21 100644 --- a/libs/superagent/prisma/schema.prisma +++ b/libs/superagent/prisma/schema.prisma @@ -23,6 +23,7 @@ enum LLMProvider { PERPLEXITY TOGETHER_AI ANTHROPIC + BEDROCK } enum LLMModel { diff --git a/libs/superagent/pyproject.toml b/libs/superagent/pyproject.toml index dda6a8408..63c75df77 100644 --- a/libs/superagent/pyproject.toml +++ b/libs/superagent/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "superagent" -version = "0.2.27" +version = "0.2.29" description = "🥷 Run AI-agents with an API" authors = ["Ismail Pelaseyed"] readme = "../../README.md" @@ -50,7 +50,7 @@ openai = "^1.1.1" langchain-experimental = "^0.0.37" pydub = "^0.25.1" algoliasearch = "^3.0.0" -litellm = "^1.29.4" +litellm = "1.35.2" weaviate-client = "^3.25.3" qdrant-client = "^1.6.9" vecs = "^0.4.2" @@ -66,6 +66,7 @@ langsmith = "^0.1.9" langfuse = "2.21.3" tavily-python = "^0.3.1" scrapingbee = "^2.0.1" +boto3 = "^1.34.83" diff --git a/libs/ui/app/integrations/llm.tsx b/libs/ui/app/integrations/llm.tsx index 26d13166a..92ff7af77 100644 --- a/libs/ui/app/integrations/llm.tsx +++ b/libs/ui/app/integrations/llm.tsx @@ -36,9 +36,17 @@ const azureSchema = z.object({ azure_deployment: z.string().nonempty("Deployment cannnot be empty"), }) +const bedrockSchema = z.object({ + aws_access_key_id: z.string().nonempty("Access key ID cannot be empty"), + aws_secret_access_key: z + .string() + .nonempty("Secret access key cannot be empty"), + aws_region: z.string().nonempty("Region cannot be empty"), +}) + const formSchema = z.object({ - apiKey: z.string().nonempty("API key is mandatory"), - options: azureSchema.optional(), + apiKey: z.string().optional(), + options: z.union([azureSchema, bedrockSchema]), }) export default function LLM({ @@ -59,6 +67,7 @@ export default function LLM({ }, }) + console.log("form", form.formState.errors) async function onSubmit(values: z.infer) { const payload = { ...values, diff --git a/libs/ui/config/site.ts b/libs/ui/config/site.ts index c4f006707..d03c066f5 100644 --- a/libs/ui/config/site.ts +++ b/libs/ui/config/site.ts @@ -249,7 +249,7 @@ export const siteConfig = { }, ], }, - { + { value: "ADVANCED_SCRAPER", title: "Advanced Web extractor", metadata: [ @@ -486,15 +486,25 @@ export const siteConfig = { ], }, { - disabled: true, - formDescription: "Please enter your HF API key.", - provider: "HUGGINGFACE", - name: "Hugging Face", + disabled: false, + formDescription: "Please enter your AWS credentials.", + provider: "BEDROCK", + name: "Amazon Bedrock", metadata: [ { - key: "apiKey", + key: "options.aws_access_key_id", type: "input", - label: "HF API Key", + label: "AWS Access Key", + }, + { + key: "options.aws_secret_access_key", + type: "input", + label: "AWS Secret Access Key", + }, + { + key: "options.aws_region_name", + type: "input", + label: "AWS Region", }, ], }, @@ -526,6 +536,19 @@ export const siteConfig = { }, ], }, + { + disabled: true, + formDescription: "Please enter your HF API key.", + provider: "HUGGINGFACE", + name: "Hugging Face", + metadata: [ + { + key: "apiKey", + type: "input", + label: "HF API Key", + }, + ], + }, ], vectorDbs: [ { From 2a57861af3bc7c1b8443ede832e83a87feb821a9 Mon Sep 17 00:00:00 2001 From: Ismail Pelaseyed Date: Fri, 12 Apr 2024 20:48:59 -0700 Subject: [PATCH 2/8] Update version --- fern/apis/prod/openapi/openapi.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fern/apis/prod/openapi/openapi.yaml b/fern/apis/prod/openapi/openapi.yaml index ee1cf32f5..27aba78d1 100644 --- a/fern/apis/prod/openapi/openapi.yaml +++ b/fern/apis/prod/openapi/openapi.yaml @@ -2,7 +2,7 @@ openapi: 3.0.2 info: title: Superagent description: 🥷 Run AI-agents with an API - version: 0.2.27 + version: 0.2.29 servers: - url: https://api.beta.superagent.sh paths: @@ -195,8 +195,8 @@ paths: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] - x-fern-sdk-method-name: invoke x-fern-sdk-group-name: agent + x-fern-sdk-method-name: invoke /api/v1/agents/{agent_id}/llms: post: tags: @@ -1782,6 +1782,7 @@ components: - PERPLEXITY - TOGETHER_AI - ANTHROPIC + - BEDROCK type: string description: An enumeration. OpenAiAssistantParameters: From 1206c60365957fcc297c6a290b9cbbf70cc02a85 Mon Sep 17 00:00:00 2001 From: Ali Salimli <67149699+elisalimli@users.noreply.github.com> Date: Sat, 13 Apr 2024 07:57:20 +0400 Subject: [PATCH 3/8] [feat] Disconnecting a Vector Database Provider (#957) * feat: add delete endpoint for Vector Database * feat(ui): add delete functionality for vector database --- libs/superagent/app/api/vector_dbs.py | 11 +++++ libs/ui/app/integrations/storage.tsx | 65 ++++++++++++++++++++------- libs/ui/lib/api.ts | 6 +++ 3 files changed, 65 insertions(+), 17 deletions(-) diff --git a/libs/superagent/app/api/vector_dbs.py b/libs/superagent/app/api/vector_dbs.py index 6d9de4682..fd7e3336a 100644 --- a/libs/superagent/app/api/vector_dbs.py +++ b/libs/superagent/app/api/vector_dbs.py @@ -102,3 +102,14 @@ async def update( return {"success": True, "data": data} except Exception as e: handle_exception(e) + + +@router.delete( + "/vector-dbs/{vector_db_id}", + name="delete", + description="Delete a Vector Database", +) +async def delete(vector_db_id: str, api_user=Depends(get_current_api_user)): + """Endpoint for deleting a Vector Database""" + await prisma.vectordb.delete(where={"id": vector_db_id, "apiUserId": api_user.id}) + return {"success": True} diff --git a/libs/ui/app/integrations/storage.tsx b/libs/ui/app/integrations/storage.tsx index fcf52a41d..7f30c0183 100644 --- a/libs/ui/app/integrations/storage.tsx +++ b/libs/ui/app/integrations/storage.tsx @@ -3,12 +3,14 @@ import * as React from "react" import Image from "next/image" import { useRouter } from "next/navigation" +import { VectorDb } from "@/models/models" import { zodResolver } from "@hookform/resolvers/zod" import { useForm } from "react-hook-form" import * as z from "zod" import { siteConfig } from "@/config/site" import { Api } from "@/lib/api" +import { cn } from "@/lib/utils" import { Button } from "@/components/ui/button" import { Dialog, @@ -29,8 +31,9 @@ import { FormMessage, } from "@/components/ui/form" import { Input } from "@/components/ui/input" -import { Skeleton } from "@/components/ui/skeleton" import { Spinner } from "@/components/ui/spinner" +import { Toaster } from "@/components/ui/toaster" +import { useToast } from "@/components/ui/use-toast" const pineconeSchema = z.object({ PINECONE_API_KEY: z.string(), @@ -83,6 +86,7 @@ export default function Storage({ const [open, setOpen] = React.useState() const [selectedDB, setSelectedDB] = React.useState() const router = useRouter() + const { toast } = useToast() const api = new Api(profile.api_key) const { ...form } = useForm>({ resolver: zodResolver(formSchema), @@ -116,6 +120,20 @@ export default function Storage({ setOpen(false) } + const onDelete = async (vectorDb: VectorDb) => { + await api.deleteVectorDb(vectorDb.id) + router.refresh() + + let providerName = vectorDb.provider.toLowerCase() + + providerName = providerName.charAt(0).toUpperCase() + providerName.slice(1) + + toast({ + title: "Success", + description: `Successfully disconnected: ${providerName}`, + }) + } + return (
@@ -127,7 +145,7 @@ export default function Storage({
{siteConfig.vectorDbs.map((vectorDb) => { - const isConfigured = configuredDBs.find( + const currentDB = configuredDBs.find( (db: any) => db.provider === vectorDb.provider ) @@ -137,11 +155,12 @@ export default function Storage({ key={vectorDb.provider} >
- {isConfigured ? ( -
- ) : ( -
- )} +
{vectorDb.name}

- + {currentDB ? ( + + ) : ( + + )}
) })} @@ -241,6 +271,7 @@ export default function Storage({
+
) } diff --git a/libs/ui/lib/api.ts b/libs/ui/lib/api.ts index 5533d48c3..d3359f5ce 100644 --- a/libs/ui/lib/api.ts +++ b/libs/ui/lib/api.ts @@ -299,4 +299,10 @@ export class Api { body: JSON.stringify(payload), }) } + + async deleteVectorDb(id: string) { + return this.fetchFromApi(`/vector-dbs/${id}`, { + method: "DELETE", + }) + } } From 1d878ba90959cbc056360b21b0762459ed6eb5da Mon Sep 17 00:00:00 2001 From: Taylor Schneider <67472065+taylor-schneider@users.noreply.github.com> Date: Fri, 12 Apr 2024 22:59:17 -0500 Subject: [PATCH 4/8] Adding missing npx command to allow container to build and run properly. (#958) Co-authored-by: Super User --- libs/ui/package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libs/ui/package.json b/libs/ui/package.json index 9638565ef..e5769d091 100644 --- a/libs/ui/package.json +++ b/libs/ui/package.json @@ -12,13 +12,13 @@ "typecheck": "tsc --noEmit", "format:write": "prettier --write \"**/*.{ts,tsx,mdx}\" --cache", "format:check": "prettier --check \"**/*.{ts,tsx,mdx}\" --cache", - "supabase:init": "supabase init", - "supabase:local:start": "supabase start && supabase status", - "supabase:local:migrate": "supabase migrations up", - "supabase:cloud:login": "supabase login", - "supabase:cloud:link": "supabase link", - "supabase:cloud:push": "supabase db push", - "supabase:docker:push": "supabase db push --db-url ${SUPABASE_DB_URL}", + "supabase:init": "npx supabase init", + "supabase:local:start": "npx supabase start && supabase status", + "supabase:local:migrate": "npx supabase migrations up", + "supabase:cloud:login": "npx supabase login", + "supabase:cloud:link": "npx supabase link", + "supabase:cloud:push": "npx supabase db push", + "supabase:docker:push": "npx supabase db push --db-url ${SUPABASE_DB_URL}", "stripe:listen": "stripe listen --forward-to http://localhost:3000/api/stripe/webhook" }, "dependencies": { From b920b24ed5cc61dae69c93d799c806f0dc76de8d Mon Sep 17 00:00:00 2001 From: alisalim17 Date: Sat, 13 Apr 2024 18:03:29 +0400 Subject: [PATCH 5/8] refactor: superrag service --- .../api/api_datasource_superrag_manager.py | 4 +- .../api/workflow_configs/data_transformer.py | 43 ++------- .../app/api/workflow_configs/exceptions.py | 6 ++ .../api/workflow_configs/workflow_configs.py | 2 +- libs/superagent/services/superrag.py | 88 ++++++++++++++++--- 5 files changed, 91 insertions(+), 52 deletions(-) create mode 100644 libs/superagent/app/api/workflow_configs/exceptions.py diff --git a/libs/superagent/app/api/workflow_configs/api/api_datasource_superrag_manager.py b/libs/superagent/app/api/workflow_configs/api/api_datasource_superrag_manager.py index 9c416756f..4f50951e0 100644 --- a/libs/superagent/app/api/workflow_configs/api/api_datasource_superrag_manager.py +++ b/libs/superagent/app/api/workflow_configs/api/api_datasource_superrag_manager.py @@ -143,7 +143,7 @@ async def add_datasource(self, assistant: dict, data: dict): data["index_name"] = await self._get_unique_index_name(data, assistant) await self._add_superrag_tool(assistant, data) - await self.superrag_service.aingest(data=data) + self.superrag_service.ingest(data=data) async def delete_datasource(self, assistant: dict, datasource: dict): tool = await self.agent_manager.get_tool( @@ -156,7 +156,7 @@ async def delete_datasource(self, assistant: dict, datasource: dict): tool_metadata = json.loads(tool.metadata) await self._delete_tool(assistant, datasource) - await self.superrag_service.adelete( + self.superrag_service.delete( { **datasource, "index_name": tool_metadata.get("index_name"), diff --git a/libs/superagent/app/api/workflow_configs/data_transformer.py b/libs/superagent/app/api/workflow_configs/data_transformer.py index be18cfc54..30158d17f 100644 --- a/libs/superagent/app/api/workflow_configs/data_transformer.py +++ b/libs/superagent/app/api/workflow_configs/data_transformer.py @@ -2,10 +2,10 @@ import logging from app.api.workflow_configs.api.api_manager import ApiManager +from app.api.workflow_configs.exceptions import MissingVectorDatabaseProvider from app.api.workflow_configs.saml_schema import SAML_OSS_LLM_PROVIDERS from app.utils.helpers import ( get_first_non_null_key, - get_mimetype_from_url, get_superrag_compatible_credentials, remove_key_if_present, rename_and_remove_keys, @@ -13,6 +13,7 @@ from app.utils.llm import LLM_REVERSE_MAPPING, get_llm_provider from app.vectorstores.base import REVERSE_VECTOR_DB_MAPPING from prisma.enums import AgentType, ToolType +from services.superrag import File logger = logging.getLogger(__name__) @@ -24,24 +25,6 @@ } -SUPERRAG_MIME_TYPE_TO_EXTENSION = { - "text/plain": "TXT", - "text/markdown": "MARKDOWN", - "application/pdf": "PDF", - "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "DOCX", - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "XLSX", - "text/csv": "CSV", -} - - -class MissingVectorDatabaseProvider(Exception): - pass - - -class UnkownFileType(Exception): - pass - - # Source https://stackoverflow.com/questions/33797126/proper-way-to-remove-keys-in-dictionary-with-none-values-in-python def delete_none_values(_dict): """Delete None values recursively from all of the dictionaries""" @@ -198,7 +181,7 @@ async def _set_database_provider(self, datasource: dict): } else: raise MissingVectorDatabaseProvider( - "Vector database provider not found." + "Vector database provider not found. " "Please configure it by going to the integrations page" ) remove_key_if_present(datasource, "database_provider") @@ -208,27 +191,13 @@ async def _set_superrag_files(self, datasource: dict): files = [] for url in urls: - file_type = self._get_file_type(url) + file = File(url=url) files.append( { - "type": file_type, - "url": url, + "type": file.type.value, + "url": file.url, } ) datasource["files"] = files remove_key_if_present(datasource, "urls") - - def _get_file_type(self, url: str): - try: - file_type = SUPERRAG_MIME_TYPE_TO_EXTENSION[get_mimetype_from_url(url)] - except KeyError: - supported_file_types = ", ".join( - value for value in SUPERRAG_MIME_TYPE_TO_EXTENSION.values() - ) - raise UnkownFileType( - f"Unknown file type for URL {url}" - f"Supported file types are: {supported_file_types}" - ) - - return file_type diff --git a/libs/superagent/app/api/workflow_configs/exceptions.py b/libs/superagent/app/api/workflow_configs/exceptions.py new file mode 100644 index 000000000..cd918b5cb --- /dev/null +++ b/libs/superagent/app/api/workflow_configs/exceptions.py @@ -0,0 +1,6 @@ +class MissingVectorDatabaseProvider(Exception): + pass + + +class UnkownFileType(Exception): + pass diff --git a/libs/superagent/app/api/workflow_configs/workflow_configs.py b/libs/superagent/app/api/workflow_configs/workflow_configs.py index c2ef03d3d..5582fb425 100644 --- a/libs/superagent/app/api/workflow_configs/workflow_configs.py +++ b/libs/superagent/app/api/workflow_configs/workflow_configs.py @@ -10,7 +10,7 @@ from app.api.workflow_configs.api.api_agent_manager import ApiAgentManager from app.api.workflow_configs.api.api_manager import ApiManager -from app.api.workflow_configs.data_transformer import ( +from app.api.workflow_configs.exceptions import ( MissingVectorDatabaseProvider, UnkownFileType, ) diff --git a/libs/superagent/services/superrag.py b/libs/superagent/services/superrag.py index f2e0b8941..338b5a50f 100644 --- a/libs/superagent/services/superrag.py +++ b/libs/superagent/services/superrag.py @@ -1,8 +1,75 @@ +from enum import Enum from typing import Optional +from urllib.parse import unquote, urlparse -import aiohttp import requests from decouple import config +from pydantic import BaseModel + +from app.api.workflow_configs.exceptions import UnkownFileType + + +# Source https://github.com/superagent-ai/super-rag/blob/bb1630be981b075d8be0585adfc2e697105f510f/models/file.py +class FileType(Enum): + pdf = "PDF" + docx = "DOCX" + txt = "TXT" + pptx = "PPTX" + md = "MARKDOWN" + csv = "CSV" + xlsx = "XLSX" + html = "HTML" + json = "JSON" + eml = "EML" + msg = "MSG" + + def suffix(self) -> str: + suffixes = { + "TXT": ".txt", + "PDF": ".pdf", + "MARKDOWN": ".md", + "DOCX": ".docx", + "CSV": ".csv", + "XLSX": ".xlsx", + "PPTX": ".pptx", + "HTML": ".html", + "JSON": ".json", + "EML": ".eml", + "MSG": ".msg", + } + return suffixes[self.value] + + +class File(BaseModel): + url: str + name: str | None = None + + @property + def type(self) -> FileType | None: + url = self.url + if url: + parsed_url = urlparse(url) + path = unquote(parsed_url.path) + extension = path.split(".")[-1].lower() + try: + return FileType[extension] + except KeyError: + supported_file_types = ", ".join( + [file_type.value for file_type in FileType] + ) + raise UnkownFileType( + f"Unknown file type for URL {url}. " + f"Supported file types are: {supported_file_types}" + ) + return None + + @property + def suffix(self) -> str: + file_type = self.type + if file_type is not None: + return file_type.suffix() + else: + raise ValueError("File type is undefined, cannot determine suffix.") class SuperRagService: @@ -12,25 +79,22 @@ def __init__(self, url: Optional[str] = None): if not self.url: raise ValueError("SUPERRAG_API_URL is not set") - async def _arequest(self, method, endpoint, data): - async with aiohttp.ClientSession() as session: - async with session.request( - method, f"{self.url}/{endpoint}", json=data - ) as response: - return await response.json() - def _request(self, method, endpoint, data): return requests.request(method, f"{self.url}/{endpoint}", json=data).json() - async def aingest(self, data): - return await self._arequest( + def ingest(self, data): + return self._request( "POST", "ingest", data, ) - async def adelete(self, data): - return await self._arequest("DELETE", "delete", data) + def delete(self, data): + return self._request( + "DELETE", + "delete", + data, + ) def query(self, data): return self._request( From 94e1544ddc2016e337cf36d68344dc7002b80529 Mon Sep 17 00:00:00 2001 From: Ali Salimli <67149699+elisalimli@users.noreply.github.com> Date: Sat, 13 Apr 2024 20:15:17 +0400 Subject: [PATCH 6/8] fix: some llm records remain unchanged (#961) --- libs/ui/app/integrations/llm.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/libs/ui/app/integrations/llm.tsx b/libs/ui/app/integrations/llm.tsx index 92ff7af77..a568f0fae 100644 --- a/libs/ui/app/integrations/llm.tsx +++ b/libs/ui/app/integrations/llm.tsx @@ -46,7 +46,7 @@ const bedrockSchema = z.object({ const formSchema = z.object({ apiKey: z.string().optional(), - options: z.union([azureSchema, bedrockSchema]), + options: z.union([azureSchema, bedrockSchema]).optional(), }) export default function LLM({ @@ -67,7 +67,6 @@ export default function LLM({ }, }) - console.log("form", form.formState.errors) async function onSubmit(values: z.infer) { const payload = { ...values, @@ -77,14 +76,16 @@ export default function LLM({ : values.options, } - const isExistingConnection = configuredLLMs.find( + const currentProviderLLMs = configuredLLMs.filter( (db: any) => db.provider === selectedProvider.provider ) - if (isExistingConnection) { - await api.patchLLM(isExistingConnection.id, { - ...payload, - provider: selectedProvider.provider, + if (currentProviderLLMs.length > 0) { + currentProviderLLMs.forEach(async (llm: any) => { + await api.patchLLM(llm.id, { + ...payload, + provider: selectedProvider.provider, + }) }) } else { await api.createLLM({ ...payload, provider: selectedProvider.provider }) From e293d6105c9826120a00ec0cb1e04a12005c9486 Mon Sep 17 00:00:00 2001 From: alisalim17 Date: Mon, 15 Apr 2024 18:11:56 +0400 Subject: [PATCH 7/8] refactor: llm integrations form --- libs/ui/app/integrations/llm.tsx | 75 +++++++++++++++++++++++--------- libs/ui/config/site.ts | 61 ++++++++++++++++---------- libs/ui/models/models.ts | 15 ++++--- 3 files changed, 103 insertions(+), 48 deletions(-) diff --git a/libs/ui/app/integrations/llm.tsx b/libs/ui/app/integrations/llm.tsx index 92ff7af77..0bbb92cdc 100644 --- a/libs/ui/app/integrations/llm.tsx +++ b/libs/ui/app/integrations/llm.tsx @@ -2,11 +2,12 @@ import * as React from "react" import { useRouter } from "next/navigation" +import { LLMProvider } from "@/models/models" import { zodResolver } from "@hookform/resolvers/zod" import { useForm } from "react-hook-form" import * as z from "zod" -import { siteConfig } from "@/config/site" +import { LLMForm, siteConfig } from "@/config/site" import { Api } from "@/lib/api" import { Button } from "@/components/ui/button" import { @@ -30,25 +31,58 @@ import { import { Input } from "@/components/ui/input" import { Spinner } from "@/components/ui/spinner" -const azureSchema = z.object({ - azure_endpoint: z.string().nonempty("Endpoint cannot be empty"), - openai_api_version: z.string().nonempty("API version cannnot be empty"), - azure_deployment: z.string().nonempty("Deployment cannnot be empty"), +const openAiSchema = z.object({ + llmType: z.literal(LLMProvider.OPENAI), + apiKey: z.string().nonempty("API key is required"), + options: z.object({}), }) -const bedrockSchema = z.object({ - aws_access_key_id: z.string().nonempty("Access key ID cannot be empty"), - aws_secret_access_key: z - .string() - .nonempty("Secret access key cannot be empty"), - aws_region: z.string().nonempty("Region cannot be empty"), +const perplexityAiSchema = z.object({ + llmType: z.literal(LLMProvider.PERPLEXITY), + apiKey: z.string().nonempty("API key is required"), + options: z.object({}), }) -const formSchema = z.object({ - apiKey: z.string().optional(), - options: z.union([azureSchema, bedrockSchema]), +const togetherAiSchema = z.object({ + llmType: z.literal(LLMProvider.TOGETHER_AI), + apiKey: z.string().nonempty("API key is required"), + options: z.object({}), }) +const antrophicSchema = z.object({ + llmType: z.literal(LLMProvider.ANTHROPIC), + apiKey: z.string().nonempty("API key is required"), + options: z.object({}), +}) +const amazonBedrockSchema = z.object({ + llmType: z.literal(LLMProvider.BEDROCK), + apiKey: z.literal(""), + options: z.object({ + aws_access_key_id: z.string(), + aws_secret_access_key: z.string(), + aws_region_name: z.string(), + }), +}) + +const azureOpenAiSchema = z.object({ + llmType: z.literal(LLMProvider.AZURE_OPENAI), + apiKey: z.string().nonempty("API key is required"), + options: z.object({ + azure_endpoint: z.string(), + openai_api_version: z.string(), + azure_deployment: z.string(), + }), +}) + +const formSchema = z.discriminatedUnion("llmType", [ + openAiSchema, + perplexityAiSchema, + togetherAiSchema, + antrophicSchema, + amazonBedrockSchema, + azureOpenAiSchema, +]) + export default function LLM({ profile, configuredLLMs, @@ -57,13 +91,15 @@ export default function LLM({ configuredLLMs: any }) { const [open, setOpen] = React.useState() - const [selectedProvider, setSelectedProvider] = React.useState() + const [selectedProvider, setSelectedProvider] = React.useState() const router = useRouter() const api = new Api(profile.api_key) const { ...form } = useForm>({ resolver: zodResolver(formSchema), - defaultValues: { + values: { + llmType: selectedProvider?.provider ?? "OPENAI", apiKey: "", + options: {} as any, }, }) @@ -78,23 +114,22 @@ export default function LLM({ } const isExistingConnection = configuredLLMs.find( - (db: any) => db.provider === selectedProvider.provider + (db: any) => db.provider === selectedProvider?.provider ) if (isExistingConnection) { await api.patchLLM(isExistingConnection.id, { ...payload, - provider: selectedProvider.provider, + provider: selectedProvider?.provider, }) } else { - await api.createLLM({ ...payload, provider: selectedProvider.provider }) + await api.createLLM({ ...payload, provider: selectedProvider?.provider }) } form.reset() router.refresh() setOpen(false) } - return (
diff --git a/libs/ui/config/site.ts b/libs/ui/config/site.ts index d03c066f5..efca406d8 100644 --- a/libs/ui/config/site.ts +++ b/libs/ui/config/site.ts @@ -1,4 +1,4 @@ -import { VectorDbProvider } from "@/models/models" +import { LLMProvider, VectorDbProvider } from "@/models/models" import { TbBrandDiscord, TbFileCode, @@ -9,7 +9,24 @@ import { TbUserCircle, } from "react-icons/tb" -export type SiteConfig = typeof siteConfig +export type SiteConfig = typeof siteConfig & { + llmForm: LLMForm +} + +export type LLMForm = { + disabled: boolean + formDescription: string + provider: keyof typeof LLMProvider + name: string + metadata: { + key: string + type: "input" | "password" | "json" | "select" + label: string + helpText?: string + json?: Record + options?: { value: string; title: string }[] + }[] +}[] export const siteConfig = { name: "Superagent Cloud", @@ -436,7 +453,7 @@ export const siteConfig = { { disabled: false, formDescription: "Please enter your OpenAI API key.", - provider: "OPENAI", + provider: LLMProvider.OPENAI, name: "OpenAI", metadata: [ { @@ -449,7 +466,7 @@ export const siteConfig = { { disabled: false, formDescription: "Please enter your Perplexity API key.", - provider: "PERPLEXITY", + provider: LLMProvider.PERPLEXITY, name: "Perplexity AI", metadata: [ { @@ -462,7 +479,7 @@ export const siteConfig = { { disabled: false, formDescription: "Please enter your Together API key.", - provider: "TOGETHER_AI", + provider: LLMProvider.TOGETHER_AI, name: "Together AI", metadata: [ { @@ -475,7 +492,7 @@ export const siteConfig = { { disabled: false, formDescription: "Please enter your Anthropic API key.", - provider: "ANTHROPIC", + provider: LLMProvider.ANTHROPIC, name: "Anthropic", metadata: [ { @@ -488,7 +505,7 @@ export const siteConfig = { { disabled: false, formDescription: "Please enter your AWS credentials.", - provider: "BEDROCK", + provider: LLMProvider.BEDROCK, name: "Amazon Bedrock", metadata: [ { @@ -511,7 +528,7 @@ export const siteConfig = { { disabled: false, formDescription: "Please enter your Azure OpenAI API key.", - provider: "AZURE_OPENAI", + provider: LLMProvider.AZURE_OPENAI, name: "Azure OpenAI", metadata: [ { @@ -536,20 +553,20 @@ export const siteConfig = { }, ], }, - { - disabled: true, - formDescription: "Please enter your HF API key.", - provider: "HUGGINGFACE", - name: "Hugging Face", - metadata: [ - { - key: "apiKey", - type: "input", - label: "HF API Key", - }, - ], - }, - ], + // { + // disabled: true, + // formDescription: "Please enter your HF API key.", + // provider: LLMProvider.HUGGINGFACE, + // name: "Hugging Face", + // metadata: [ + // { + // key: "apiKey", + // type: "input", + // label: "HF API Key", + // }, + // ], + // }, + ] satisfies LLMForm, vectorDbs: [ { provider: VectorDbProvider[VectorDbProvider.PINECONE], diff --git a/libs/ui/models/models.ts b/libs/ui/models/models.ts index 188166a3e..414c0758a 100644 --- a/libs/ui/models/models.ts +++ b/libs/ui/models/models.ts @@ -1,8 +1,11 @@ -export enum LLMProvider { - OPENAI = "OPENAI", - AZURE_OPENAI = "AZURE_OPENAI", - HUGGINGFACE = "HUGGINGFACE", -} +export const LLMProvider = { + OPENAI: "OPENAI", + PERPLEXITY: "PERPLEXITY", + TOGETHER_AI: "TOGETHER_AI", + ANTHROPIC: "ANTHROPIC", + BEDROCK: "BEDROCK", + AZURE_OPENAI: "AZURE_OPENAI", +} as const export enum LLMModel { GPT_3_5_TURBO_16K_0613, @@ -252,7 +255,7 @@ export class AgentTool { export class LLM { id: string - provider: LLMProvider + provider: typeof LLMProvider apiKey: string options?: JSON agents: AgentLLM[] From 2e7b98393b6b510f62c89f333e5cd002d7a53022 Mon Sep 17 00:00:00 2001 From: alisalim17 Date: Mon, 15 Apr 2024 18:20:02 +0400 Subject: [PATCH 8/8] refactor: llmProvider type in add-datasource.tsx and settings.tsx --- libs/ui/app/agents/[agentId]/add-datasource.tsx | 6 ++++-- libs/ui/app/agents/[agentId]/settings.tsx | 2 +- libs/ui/app/integrations/llm.tsx | 2 +- libs/ui/config/site.ts | 4 +--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libs/ui/app/agents/[agentId]/add-datasource.tsx b/libs/ui/app/agents/[agentId]/add-datasource.tsx index 4c3cd77d2..6e637490b 100644 --- a/libs/ui/app/agents/[agentId]/add-datasource.tsx +++ b/libs/ui/app/agents/[agentId]/add-datasource.tsx @@ -51,7 +51,7 @@ interface AddDatasourceProps { profile: any agent: any onSuccess: () => void - llmProvider: LLMProvider + llmProvider: keyof typeof LLMProvider } const supabase = getSupabase() @@ -111,7 +111,9 @@ function AddDatasource({ } } - function getEmbeddingsModelProvider(llmProvider: LLMProvider): LLMProvider { + function getEmbeddingsModelProvider( + llmProvider: keyof typeof LLMProvider + ): keyof typeof LLMProvider { if (llmProvider === LLMProvider.AZURE_OPENAI) return LLMProvider.AZURE_OPENAI diff --git a/libs/ui/app/agents/[agentId]/settings.tsx b/libs/ui/app/agents/[agentId]/settings.tsx index e30b5ca8c..b09f21247 100644 --- a/libs/ui/app/agents/[agentId]/settings.tsx +++ b/libs/ui/app/agents/[agentId]/settings.tsx @@ -97,7 +97,7 @@ export default function Settings({ }, }) const avatar = form.watch("avatar") - const currLlmProvider = form.watch("llms") as LLMProvider + const currLlmProvider = form.watch("llms") as keyof typeof LLMProvider async function onSubmit(values: z.infer) { const { tools, datasources } = values diff --git a/libs/ui/app/integrations/llm.tsx b/libs/ui/app/integrations/llm.tsx index 459fa9138..c7fc9a296 100644 --- a/libs/ui/app/integrations/llm.tsx +++ b/libs/ui/app/integrations/llm.tsx @@ -97,7 +97,7 @@ export default function LLM({ const { ...form } = useForm>({ resolver: zodResolver(formSchema), values: { - llmType: selectedProvider?.provider ?? "OPENAI", + llmType: selectedProvider?.provider ?? LLMProvider.OPENAI, apiKey: "", options: {} as any, }, diff --git a/libs/ui/config/site.ts b/libs/ui/config/site.ts index efca406d8..3d2088966 100644 --- a/libs/ui/config/site.ts +++ b/libs/ui/config/site.ts @@ -9,9 +9,7 @@ import { TbUserCircle, } from "react-icons/tb" -export type SiteConfig = typeof siteConfig & { - llmForm: LLMForm -} +export type SiteConfig = typeof siteConfig export type LLMForm = { disabled: boolean