Compare commits

...

2 Commits

Author SHA1 Message Date
github-actions[bot] 53e1cd56e7 Release 0.5.10 (#579)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-22 15:45:31 +07:00
Huu Le 0a2e12a2bb Use uv as the default package manager and deprecate poetry. (#578) 2025-04-22 15:44:11 +07:00
37 changed files with 443 additions and 356 deletions
+8 -11
View File
@@ -9,9 +9,6 @@ on:
paths-ignore:
- "llama-index-server/**"
env:
POETRY_VERSION: "1.6.1"
jobs:
e2e-python:
name: python
@@ -36,10 +33,10 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: ${{ env.POETRY_VERSION }}
- name: Install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Add uv to PATH # Ensure uv is available in subsequent steps
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- uses: pnpm/action-setup@v3
@@ -106,10 +103,10 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: ${{ env.POETRY_VERSION }}
- name: Install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Add uv to PATH # Ensure uv is available in subsequent steps
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- uses: pnpm/action-setup@v3
+6
View File
@@ -1,5 +1,11 @@
# create-llama
## 0.5.10
### Patch Changes
- 0a2e12a: Use uv as the default package manager
## 0.5.9
### Patch Changes
+1 -1
View File
@@ -55,7 +55,7 @@ Then re-start your app. Remember you'll need to re-run `generate` if you add new
If you're using the Python backend, you can trigger indexing of your data by calling:
```bash
poetry run generate
uv run generate
```
## Customizing the AI models
+30 -15
View File
@@ -195,32 +195,47 @@ async function createAndCheckLlamaProject({
const pyprojectPath = path.join(projectPath, "pyproject.toml");
expect(fs.existsSync(pyprojectPath)).toBeTruthy();
const env = {
// Modify environment for the command
const commandEnv = {
...process.env,
POETRY_VIRTUALENVS_IN_PROJECT: "true",
};
// Run poetry install
console.log("Running uv venv...");
try {
const { stdout: installStdout, stderr: installStderr } = await execAsync(
"poetry install",
{ cwd: projectPath, env },
const { stdout: venvStdout, stderr: venvStderr } = await execAsync(
"uv venv",
{ cwd: projectPath, env: commandEnv },
);
console.log("poetry install stdout:", installStdout);
console.error("poetry install stderr:", installStderr);
console.log("uv venv stdout:", venvStdout);
console.error("uv venv stderr:", venvStderr);
} catch (error) {
console.error("Error running poetry install:", error);
throw error;
console.error("Error running uv venv:", error);
throw error; // Re-throw error to fail the test
}
// Run poetry run mypy
console.log("Running uv sync...");
try {
const { stdout: syncStdout, stderr: syncStderr } = await execAsync(
"uv sync --all-extras",
{ cwd: projectPath, env: commandEnv },
);
console.log("uv sync stdout:", syncStdout);
console.error("uv sync stderr:", syncStderr);
} catch (error) {
console.error("Error running uv sync:", error);
throw error; // Re-throw error to fail the test
}
console.log("Running uv run mypy ....");
try {
const { stdout: mypyStdout, stderr: mypyStderr } = await execAsync(
"poetry run mypy .",
{ cwd: projectPath, env },
"uv run mypy .",
{ cwd: projectPath, env: commandEnv },
);
console.log("poetry run mypy stdout:", mypyStdout);
console.error("poetry run mypy stderr:", mypyStderr);
console.log("uv run mypy stdout:", mypyStdout);
console.error("uv run mypy stderr:", mypyStderr);
// Assuming mypy success means no output or specific success message
// Adjust checks based on actual expected mypy output
} catch (error) {
console.error("Error running mypy:", error);
throw error;
+11 -5
View File
@@ -9,7 +9,6 @@ import { createBackendEnvFile, createFrontendEnvFile } from "./env-variables";
import { PackageManager } from "./get-pkg-manager";
import { installLlamapackProject } from "./llama-pack";
import { makeDir } from "./make-dir";
import { isHavingPoetryLockFile, tryPoetryRun } from "./poetry";
import { installPythonTemplate } from "./python";
import { downloadAndExtractRepo } from "./repo";
import { ConfigFileType, writeToolsConfig } from "./tools";
@@ -22,6 +21,7 @@ import {
TemplateVectorDB,
} from "./types";
import { installTSTemplate } from "./typescript";
import { isHavingUvLockFile, tryUvRun } from "./uv";
const checkForGenerateScript = (
modelConfig: ModelConfig,
@@ -64,7 +64,7 @@ async function generateContextData(
if (packageManager) {
const runGenerate = `${cyan(
framework === "fastapi"
? "poetry run generate"
? "uv run generate"
: `${packageManager} run generate`,
)}`;
@@ -78,15 +78,21 @@ async function generateContextData(
if (!missingSettings.length) {
// If all the required environment variables are set, run the generate script
if (framework === "fastapi") {
if (isHavingPoetryLockFile()) {
if (isHavingUvLockFile()) {
console.log(`Running ${runGenerate} to generate the context data.`);
const result = tryPoetryRun("poetry run generate");
const result = tryUvRun("generate");
if (!result) {
console.log(`Failed to run ${runGenerate}.`);
process.exit(1);
}
console.log(`Generated context data`);
return;
} else {
console.log(
picocolors.yellow(
`\nWarning: uv.lock not found. Dependency installation might be incomplete. Skipping context generation.\nIf dependencies were installed, try running '${runGenerate}' manually.\n`,
),
);
}
} else {
console.log(`Running ${runGenerate} to generate the context data.`);
@@ -103,7 +109,7 @@ async function generateContextData(
const downloadFile = async (url: string, destPath: string) => {
const response = await fetch(url);
const fileBuffer = await response.arrayBuffer();
await fsExtra.writeFile(destPath, Buffer.from(fileBuffer));
await fsExtra.writeFile(destPath, new Uint8Array(fileBuffer));
};
const prepareContextData = async (
+1 -1
View File
@@ -143,6 +143,6 @@ export const installLlamapackProject = async ({
await copyData({ root });
await installLlamapackExample({ root, llamapack });
if (postInstallAction === "runApp" || postInstallAction === "dependencies") {
installPythonDependencies({ noRoot: true });
installPythonDependencies();
}
};
+135 -104
View File
@@ -3,10 +3,10 @@ import path from "path";
import { cyan, red } from "picocolors";
import { parse, stringify } from "smol-toml";
import terminalLink from "terminal-link";
import { isUvAvailable, tryUvSync } from "./uv";
import { assetRelocator, copy } from "./copy";
import { templatesDir } from "./dir";
import { isPoetryAvailable, tryPoetryInstall } from "./poetry";
import { Tool } from "./tools";
import {
InstallTemplateArgs,
@@ -39,21 +39,21 @@ const getAdditionalDependencies = (
case "mongo": {
dependencies.push({
name: "llama-index-vector-stores-mongodb",
version: "^0.6.0",
version: ">=0.3.2,<0.4.0",
});
break;
}
case "pg": {
dependencies.push({
name: "llama-index-vector-stores-postgres",
version: "^0.3.2",
version: ">=0.3.2,<0.4.0",
});
break;
}
case "pinecone": {
dependencies.push({
name: "llama-index-vector-stores-pinecone",
version: "^0.4.1",
version: ">=0.4.1,<0.5.0",
constraints: {
python: ">=3.11,<3.13",
},
@@ -63,25 +63,25 @@ const getAdditionalDependencies = (
case "milvus": {
dependencies.push({
name: "llama-index-vector-stores-milvus",
version: "^0.3.0",
version: ">=0.3.0,<0.4.0",
});
dependencies.push({
name: "pymilvus",
version: "2.4.4",
version: ">=2.4.4,<3.0.0",
});
break;
}
case "astra": {
dependencies.push({
name: "llama-index-vector-stores-astra-db",
version: "^0.4.0",
version: ">=0.4.0,<0.5.0",
});
break;
}
case "qdrant": {
dependencies.push({
name: "llama-index-vector-stores-qdrant",
version: "^0.4.0",
version: ">=0.4.0,<0.5.0",
constraints: {
python: ">=3.11,<3.13",
},
@@ -91,21 +91,21 @@ const getAdditionalDependencies = (
case "chroma": {
dependencies.push({
name: "llama-index-vector-stores-chroma",
version: "^0.4.0",
version: ">=0.4.0,<0.5.0",
});
break;
}
case "weaviate": {
dependencies.push({
name: "llama-index-vector-stores-weaviate",
version: "^1.2.3",
version: ">=1.2.3,<2.0.0",
});
break;
}
case "llamacloud":
dependencies.push({
name: "llama-index-indices-managed-llama-cloud",
version: "0.6.3",
version: ">=0.6.3,<0.7.0",
});
break;
}
@@ -118,28 +118,28 @@ const getAdditionalDependencies = (
case "file":
dependencies.push({
name: "docx2txt",
version: "^0.8",
version: ">=0.8,<0.9",
});
break;
case "web":
dependencies.push({
name: "llama-index-readers-web",
version: "^0.3.0",
version: ">=0.3.0,<0.4.0",
});
break;
case "db":
dependencies.push({
name: "llama-index-readers-database",
version: "^0.3.0",
version: ">=0.3.0,<0.4.0",
});
dependencies.push({
name: "pymysql",
version: "^1.1.0",
version: ">=1.1.0,<2.0.0",
extras: ["rsa"],
});
dependencies.push({
name: "psycopg2-binary",
version: "^2.9.9",
version: ">=2.9.9,<3.0.0",
});
break;
}
@@ -158,114 +158,102 @@ const getAdditionalDependencies = (
case "ollama":
dependencies.push({
name: "llama-index-llms-ollama",
version: "0.3.0",
version: ">=0.5.0,<0.6.0",
});
dependencies.push({
name: "llama-index-embeddings-ollama",
version: "0.3.0",
version: ">=0.6.0,<0.7.0",
});
break;
case "openai":
if (templateType !== "multiagent") {
dependencies.push({
name: "llama-index-llms-openai",
version: "^0.3.2",
version: ">=0.3.2,<0.4.0",
});
dependencies.push({
name: "llama-index-embeddings-openai",
version: "^0.3.1",
version: ">=0.3.1,<0.4.0",
});
dependencies.push({
name: "llama-index-agent-openai",
version: "^0.4.0",
version: ">=0.4.0,<0.5.0",
});
}
break;
case "groq":
// Fastembed==0.2.0 does not support python3.13 at the moment
// Fixed the python version less than 3.13
dependencies.push({
name: "python",
version: "^3.11,<3.13",
});
dependencies.push({
name: "llama-index-llms-groq",
version: "0.2.0",
version: ">=0.3.0,<0.4.0",
});
dependencies.push({
name: "llama-index-embeddings-fastembed",
version: "^0.2.0",
version: ">=0.3.0,<0.4.0",
});
break;
case "anthropic":
// Fastembed==0.2.0 does not support python3.13 at the moment
// Fixed the python version less than 3.13
dependencies.push({
name: "python",
version: "^3.11,<3.13",
});
dependencies.push({
name: "llama-index-llms-anthropic",
version: "0.3.0",
version: ">=0.6.0,<0.7.0",
});
dependencies.push({
name: "llama-index-embeddings-fastembed",
version: "^0.2.0",
version: ">=0.3.0,<0.4.0",
});
break;
case "gemini":
dependencies.push({
name: "llama-index-llms-gemini",
version: "0.3.4",
version: ">=0.4.0,<0.5.0",
});
dependencies.push({
name: "llama-index-embeddings-gemini",
version: "^0.2.0",
version: ">=0.3.0,<0.4.0",
});
break;
case "mistral":
dependencies.push({
name: "llama-index-llms-mistralai",
version: "0.2.1",
version: ">=0.4.0,<0.5.0",
});
dependencies.push({
name: "llama-index-embeddings-mistralai",
version: "0.2.0",
version: ">=0.3.0,<0.4.0",
});
break;
case "azure-openai":
dependencies.push({
name: "llama-index-llms-azure-openai",
version: "0.2.0",
version: ">=0.3.0,<0.4.0",
});
dependencies.push({
name: "llama-index-embeddings-azure-openai",
version: "0.2.4",
version: ">=0.3.0,<0.4.0",
});
break;
case "huggingface":
dependencies.push({
name: "llama-index-llms-huggingface",
version: "^0.3.5",
version: ">=0.5.0,<0.6.0",
});
dependencies.push({
name: "llama-index-embeddings-huggingface",
version: "^0.3.1",
version: ">=0.5.0,<0.6.0",
});
dependencies.push({
name: "optimum",
version: "^1.23.3",
version: ">=1.23.3,<2.0.0",
extras: ["onnxruntime"],
});
break;
case "t-systems":
dependencies.push({
name: "llama-index-agent-openai",
version: "0.3.0",
version: ">=0.4.0,<0.5.0",
});
dependencies.push({
name: "llama-index-llms-openai-like",
version: "0.2.0",
version: ">=0.3.0,<0.4.0",
});
break;
}
@@ -274,13 +262,13 @@ const getAdditionalDependencies = (
if (observability === "traceloop") {
dependencies.push({
name: "traceloop-sdk",
version: "^0.15.11",
version: ">=0.15.11,<0.16.0",
});
}
if (observability === "llamatrace") {
dependencies.push({
name: "llama-index-callbacks-arize-phoenix",
version: "^0.3.0",
version: ">=0.3.0,<0.4.0",
});
}
}
@@ -288,42 +276,6 @@ const getAdditionalDependencies = (
return dependencies;
};
const mergePoetryDependencies = (
dependencies: Dependency[],
existingDependencies: Record<string, Omit<Dependency, "name"> | string>,
) => {
for (const dependency of dependencies) {
let value = existingDependencies[dependency.name] ?? {};
// default string value is equal to attribute "version"
if (typeof value === "string") {
value = { version: value };
}
value.version = dependency.version ?? value.version;
value.extras = dependency.extras ?? value.extras;
// Merge constraints if they exist
if (dependency.constraints) {
value = { ...value, ...dependency.constraints };
}
if (value.version === undefined) {
throw new Error(
`Dependency "${dependency.name}" is missing attribute "version"!`,
);
}
// Serialize as object if there are any additional properties
if (Object.keys(value).length > 1) {
existingDependencies[dependency.name] = value;
} else {
// Otherwise, serialize just the version string
existingDependencies[dependency.name] = value.version;
}
}
};
const copyRouterCode = async (root: string, tools: Tool[]) => {
// Copy sandbox router if the artifact tool is selected
if (tools?.some((t) => t.name === "artifact")) {
@@ -346,19 +298,100 @@ export const addDependencies = async (
// Parse toml file
const file = path.join(projectDir, FILENAME);
const fileContent = await fs.readFile(file, "utf8");
const fileParsed = parse(fileContent);
let fileParsed: any;
try {
fileParsed = parse(fileContent);
} catch (parseError) {
console.error(`Error parsing ${FILENAME}:`, parseError);
throw new Error(
`Failed to parse ${FILENAME}. Please ensure it's valid TOML.`,
);
}
// Modify toml dependencies
const tool = fileParsed.tool as any;
const existingDependencies = tool.poetry.dependencies;
mergePoetryDependencies(dependencies, existingDependencies);
// Ensure [project] and [project.dependencies] exist
if (!fileParsed.project) {
fileParsed.project = {};
}
if (
!fileParsed.project.dependencies ||
!Array.isArray(fileParsed.project.dependencies)
) {
// If dependencies exist but aren't an array, log a warning or error.
// For now, we'll overwrite it, assuming the intent is to use the standard array format.
console.warn(
`[project.dependencies] in ${FILENAME} is not an array. It will be overwritten.`,
);
fileParsed.project.dependencies = [];
}
const existingDependencies: string[] = fileParsed.project.dependencies;
const addedDeps: string[] = [];
const updatedDeps: string[] = [];
// Add or update dependencies
for (const newDep of dependencies) {
let depString = newDep.name;
if (newDep.extras && newDep.extras.length > 0) {
depString += `[${newDep.extras.join(",")}]`;
}
if (newDep.version) {
depString += newDep.version;
}
let found = false;
for (let i = 0; i < existingDependencies.length; i++) {
const existingDepNameMatch =
existingDependencies[i].match(/^([a-zA-Z0-9._-]+)/);
if (
existingDepNameMatch &&
existingDepNameMatch[1].toLowerCase() === depString.toLowerCase()
) {
// Found existing dependency, update it
if (existingDependencies[i] !== depString) {
updatedDeps.push(`${existingDependencies[i]} -> ${depString}`);
existingDependencies[i] = depString;
}
found = true;
break;
}
}
if (!found) {
// Add new dependency
existingDependencies.push(depString);
addedDeps.push(depString);
}
// Handle python version constraints separately (if any)
if (newDep.constraints?.python) {
if (
!fileParsed.project["requires-python"] ||
fileParsed.project["requires-python"] !== newDep.constraints.python
) {
// This simple overwrite might not be ideal; merging constraints is complex.
// For now, let's just set it if the new dependency has one.
console.log(
`Setting requires-python = "${newDep.constraints.python}" from dependency ${newDep.name}`,
);
fileParsed.project["requires-python"] = newDep.constraints.python;
}
}
}
// Write toml file
const newFileContent = stringify(fileParsed);
await fs.writeFile(file, newFileContent);
const dependenciesString = dependencies.map((d) => d.name).join(", ");
console.log(`\nAdded ${dependenciesString} to ${cyan(FILENAME)}\n`);
if (addedDeps.length > 0) {
console.log(`\nAdded dependencies to ${cyan(FILENAME)}:`);
addedDeps.forEach((dep) => console.log(` ${dep}`));
}
if (updatedDeps.length > 0) {
console.log(`\nUpdated dependencies in ${cyan(FILENAME)}:`);
updatedDeps.forEach((dep) => console.log(` ${dep}`));
}
if (addedDeps.length > 0 || updatedDeps.length > 0) {
console.log(""); // Newline for spacing
}
} catch (error) {
console.log(
`Error while updating dependencies for Poetry project file ${FILENAME}\n`,
@@ -367,18 +400,16 @@ export const addDependencies = async (
}
};
export const installPythonDependencies = (
{ noRoot }: { noRoot: boolean } = { noRoot: false },
) => {
if (isPoetryAvailable()) {
export const installPythonDependencies = () => {
if (isUvAvailable()) {
console.log(
`Installing python dependencies using poetry. This may take a while...`,
`Installing Python dependencies using uv. This may take a while...`,
);
const installSuccessful = tryPoetryInstall(noRoot);
const installSuccessful = tryUvSync();
if (!installSuccessful) {
console.error(
red(
"Installing dependencies using poetry failed. Please check error log above and try running create-llama again.",
"Installing dependencies using uv failed. Please check the error log above and ensure uv is installed correctly.",
),
);
process.exit(1);
@@ -386,10 +417,10 @@ export const installPythonDependencies = (
} else {
console.error(
red(
`Poetry is not available in the current environment. Please check ${terminalLink(
"Poetry Installation",
`https://python-poetry.org/docs/#installation`,
)} to install poetry first, then run create-llama again.`,
`uv is not available in the current environment. Please check ${terminalLink(
"uv Installation",
`https://github.com/astral-sh/uv#installation`,
)} to install uv first, then run create-llama again.`,
),
);
process.exit(1);
+14 -4
View File
@@ -34,14 +34,24 @@ export function runReflexApp(appPath: string, port: number) {
"--frontend-port",
port.toString(),
];
return createProcess("poetry", commandArgs, {
return createProcess("uv", commandArgs, {
stdio: "inherit",
cwd: appPath,
});
}
export function runFastAPIApp(appPath: string, port: number) {
return createProcess("poetry", ["run", "dev"], {
export function runFastAPIApp(
appPath: string,
port: number,
template: TemplateType,
) {
let commandArgs: string[];
if (template === "streaming") {
commandArgs = ["run", "dev"];
} else {
commandArgs = ["run", "fastapi", "dev", "--port", `${port}`];
}
return createProcess("uv", commandArgs, {
stdio: "inherit",
cwd: appPath,
env: { ...process.env, APP_PORT: `${port}` },
@@ -73,7 +83,7 @@ export async function runApp(
: framework === "fastapi"
? runFastAPIApp
: runTSApp;
await appRunner(appPath, port || defaultPort);
await appRunner(appPath, port || defaultPort, template);
} catch (error) {
console.error("Failed to run app:", error);
throw error;
+10 -10
View File
@@ -41,7 +41,7 @@ export const supportedTools: Tool[] = [
dependencies: [
{
name: "llama-index-tools-google",
version: "^0.3.0",
version: ">=0.3.0,<0.4.0",
},
],
supportedFrameworks: ["fastapi"],
@@ -62,7 +62,7 @@ export const supportedTools: Tool[] = [
dependencies: [
{
name: "duckduckgo-search",
version: "^6.3.5",
version: ">=6.3.5,<7.0.0",
},
],
supportedFrameworks: ["fastapi"], // TODO: Re-enable this tool once the duck-duck-scrape TypeScript library works again
@@ -82,7 +82,7 @@ For better results, you can specify the region parameter to get results from a s
dependencies: [
{
name: "llama-index-tools-wikipedia",
version: "^0.3.0",
version: ">=0.3.0,<0.4.0",
},
],
supportedFrameworks: ["fastapi", "express", "nextjs"],
@@ -102,11 +102,11 @@ For better results, you can specify the region parameter to get results from a s
dependencies: [
{
name: "xhtml2pdf",
version: "^0.2.14",
version: ">=0.2.14,<0.3.0",
},
{
name: "markdown",
version: "^3.7",
version: ">=3.7.0,<4.0.0",
},
],
type: ToolType.LOCAL,
@@ -124,7 +124,7 @@ For better results, you can specify the region parameter to get results from a s
dependencies: [
{
name: "e2b_code_interpreter",
version: "1.1.1",
version: ">=1.1.1,<1.2.0",
},
],
supportedFrameworks: ["fastapi", "express", "nextjs"],
@@ -155,7 +155,7 @@ For better results, you can specify the region parameter to get results from a s
dependencies: [
{
name: "e2b_code_interpreter",
version: "1.1.1",
version: ">=1.1.1,<1.2.0",
},
],
supportedFrameworks: ["fastapi", "express", "nextjs"],
@@ -184,7 +184,7 @@ For better results, you can specify the region parameter to get results from a s
},
{
name: "jsonschema",
version: "^4.22.0",
version: ">=4.22.0,<5.0.0",
},
{
name: "llama-index-tools-requests",
@@ -247,11 +247,11 @@ For better results, you can specify the region parameter to get results from a s
dependencies: [
{
name: "pandas",
version: "^2.2.3",
version: ">=2.2.3,<3.0.0",
},
{
name: "tabulate",
version: "^0.9.0",
version: ">=0.9.0,<1.0.0",
},
],
},
+42
View File
@@ -0,0 +1,42 @@
// Migrate poetry to uv
import { execSync } from "child_process";
import fs from "fs";
import { red } from "picocolors";
export function isUvAvailable(): boolean {
try {
execSync("uv --version", { stdio: "ignore" });
return true;
} catch (_) {}
return false;
}
export function tryUvSync(): boolean {
try {
console.log("Syncing environment with pyproject.toml...");
execSync(`uv sync`, {
stdio: "inherit",
});
return true;
} catch (_) {}
return false;
}
export function tryUvRun(command: string): boolean {
try {
// Use uv run <command>
execSync(`uv run ${command}`, { stdio: "inherit" });
return true;
} catch (error) {
console.error(red(`Failed to run ${command}. Error: ${error}`));
return false;
}
}
export function isHavingUvLockFile(): boolean {
try {
// Check if uv.lock exists in the current directory
return fs.existsSync("uv.lock");
} catch (_) {}
return false;
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "create-llama",
"version": "0.5.9",
"version": "0.5.10",
"description": "Create LlamaIndex-powered apps with one command",
"keywords": [
"rag",
@@ -19,20 +19,20 @@ First, setup the environment with poetry:
> **_Note:_** This step is not needed if you are using the dev-container.
```shell
poetry install
uv sync
```
Then check the parameters that have been pre-configured in the `.env` file in this directory. (E.g. you might need to configure an `OPENAI_API_KEY` if you're using OpenAI as model provider).
Second, generate the embeddings of the documents in the `./data` directory:
```shell
poetry run generate
uv run generate
```
Third, run the development server:
```shell
poetry run dev
uv run dev
```
Per default, the example is using the explicit workflow. You can change the example by setting the `EXAMPLE_TYPE` environment variable to `choreography` or `orchestrator`.
@@ -52,7 +52,7 @@ Open [http://localhost:8000](http://localhost:8000) with your browser to start t
To start the app optimized for **production**, run:
```
poetry run prod
uv run prod
```
## Deployments
@@ -7,20 +7,20 @@ First, setup the environment with poetry:
> **_Note:_** This step is not needed if you are using the dev-container.
```shell
poetry install
uv sync
```
Then check the parameters that have been pre-configured in the `.env` file in this directory. (E.g. you might need to configure an `OPENAI_API_KEY` if you're using OpenAI as model provider).
Second, generate the embeddings of the documents in the `./data` directory:
```shell
poetry run generate
uv run generate
```
Third, run the development server:
```shell
poetry run dev
uv run dev
```
## Use Case: Deep Research over own documents
@@ -7,7 +7,7 @@ First, setup the environment with poetry:
> **_Note:_** This step is not needed if you are using the dev-container.
```shell
poetry install
uv sync
```
Then check the parameters that have been pre-configured in the `.env` file in this directory. (E.g. you might need to configure an `OPENAI_API_KEY` if you're using OpenAI as model provider and `E2B_API_KEY` for the [E2B's code interpreter tool](https://e2b.dev/docs)).
@@ -15,13 +15,13 @@ Then check the parameters that have been pre-configured in the `.env` file in th
Second, generate the embeddings of the documents in the `./data` directory:
```shell
poetry run generate
uv run generate
```
Third, run the development server:
```shell
poetry run dev
uv run dev
```
The example provides one streaming API endpoint `/api/chat`.
@@ -40,7 +40,7 @@ Open [http://localhost:8000](http://localhost:8000) with your browser to start t
To start the app optimized for **production**, run:
```
poetry run prod
uv run prod
```
## Deployments
@@ -7,7 +7,7 @@ First, setup the environment with poetry:
> **_Note:_** This step is not needed if you are using the dev-container.
```shell
poetry install
uv sync
```
Then check the parameters that have been pre-configured in the `.env` file in this directory.
@@ -16,7 +16,7 @@ Make sure you have the `OPENAI_API_KEY` set.
Second, run the development server:
```shell
poetry run dev
uv run dev
```
## Use Case: Filling Financial CSV Template
@@ -46,7 +46,7 @@ Open [http://localhost:8000](http://localhost:8000) with your browser to start t
To start the app optimized for **production**, run:
```
poetry run prod
uv run prod
```
## Deployments
@@ -30,7 +30,7 @@ def get_chat_engine(params=None, event_handlers=None, **kwargs):
raise HTTPException(
status_code=500,
detail=str(
"StorageContext is empty - call 'poetry run generate' to generate the storage first"
"StorageContext is empty - call 'uv run generate' to generate the storage first"
),
)
if top_k != 0 and kwargs.get("similarity_top_k") is None:
+1 -1
View File
@@ -10,7 +10,7 @@ class CrawlUrl(BaseModel):
class WebLoaderConfig(BaseModel):
driver_arguments: Optional[List[str]] = Field(default_factory=list)
driver_arguments: Optional[List[str]] = Field(default=None)
urls: List[CrawlUrl]
@@ -7,7 +7,7 @@ First, setup the environment with poetry:
> **_Note:_** This step is not needed if you are using the dev-container.
```shell
poetry install
uv sync
```
Then check the parameters that have been pre-configured in the `.env` file in this directory. (E.g. you might need to configure an `OPENAI_API_KEY` if you're using OpenAI as model provider).
@@ -15,13 +15,13 @@ Then check the parameters that have been pre-configured in the `.env` file in th
Second, generate the embeddings of the example document in the `./data` directory:
```shell
poetry run generate
uv run generate
```
Third, start app with `reflex` command:
```shell
poetry run reflex run
uv run reflex run
```
To deploy the application, refer to the Reflex deployment guide: https://reflex.dev/docs/hosting/deploy-quick-start/
@@ -40,7 +40,7 @@ To get started:
2. Review Process:
- The system will automatically analyze your document against compliance guidelines
- By default, it uses [GDPR](./data/gdpr.pdf) as the compliance benchmark
- Custom guidelines can be used by adding your policy documents to the `./data` directory and running `poetry run generate` to update the embeddings
- Custom guidelines can be used by adding your policy documents to the `./data` directory and running `uv run generate` to update the embeddings
The interface will display the analysis results for the compliance of the contract document.
@@ -5,20 +5,6 @@ from enum import Enum
from pathlib import Path
from typing import List
from llama_index.core import SimpleDirectoryReader
from llama_index.core.llms import LLM
from llama_index.core.prompts import ChatPromptTemplate
from llama_index.core.retrievers import BaseRetriever
from llama_index.core.settings import Settings
from llama_index.core.workflow import (
Context,
Event,
StartEvent,
StopEvent,
Workflow,
step,
)
from app.config import (
COMPLIANCE_REPORT_SYSTEM_PROMPT,
COMPLIANCE_REPORT_USER_PROMPT,
@@ -32,6 +18,19 @@ from app.models import (
ContractClause,
ContractExtraction,
)
from llama_index.core import SimpleDirectoryReader
from llama_index.core.llms import LLM
from llama_index.core.prompts import ChatPromptTemplate
from llama_index.core.retrievers import BaseRetriever
from llama_index.core.settings import Settings
from llama_index.core.workflow import (
Context,
Event,
StartEvent,
StopEvent,
Workflow,
step,
)
logger = logging.getLogger(__name__)
@@ -40,7 +39,7 @@ def get_workflow():
index = get_index()
if index is None:
raise RuntimeError(
"Index not found! Please run `poetry run generate` to populate an index first."
"Index not found! Please run `uv run generate` to populate an index first."
)
return ContractReviewWorkflow(
guideline_retriever=index.as_retriever(),
@@ -1,44 +1,33 @@
[tool]
[tool.poetry]
[project]
name = "app"
version = "0.1.0"
description = ""
authors = [ "Marcus Schiesser <mail@marcusschiesser.de>" ]
authors = [ { name = "Marcus Schiesser", email = "mail@marcusschiesser.de" } ]
readme = "README.md"
requires-python = ">=3.11,<4.0"
dependencies = [
"fastapi>=0.109.1",
"python-dotenv>=1.0.0",
"pydantic<2.10",
"llama-index>=0.12.1",
"cachetools>=5.3.3",
"reflex>=0.6.2.post1",
]
[tool.poetry.scripts]
[project.scripts]
generate = "app.engine.generate:generate_datasource"
[tool.poetry.dependencies]
python = "^3.11,<4.0"
fastapi = "^0.109.1"
python-dotenv = "^1.0.0"
pydantic = "<2.10"
llama-index = "^0.12.1"
cachetools = "^5.3.3"
reflex = "^0.6.2.post1"
[tool.poetry.dependencies.uvicorn]
extras = [ "standard" ]
version = "^0.23.2"
[tool.poetry.dependencies.docx2txt]
version = "^0.8"
[tool.poetry.dependencies.llama-index-llms-openai]
version = "^0.3.2"
[tool.poetry.dependencies.llama-index-embeddings-openai]
version = "^0.3.1"
[tool.poetry.dependencies.llama-index-agent-openai]
version = "^0.4.0"
[tool.poetry.group.dev.dependencies]
pytest-asyncio = "^0.25.0"
pytest = "^8.3.4"
[project.optional-dependencies]
dev = [
"mypy>=1.8.0",
"pytest>=8.3.5",
"pytest-asyncio>=0.25.3",
"docx2txt>=0.8",
"llama-index-llms-openai>=0.3.2",
"llama-index-embeddings-openai>=0.3.1",
"llama-index-agent-openai>=0.4.0",
]
[build-system]
requires = [ "poetry-core" ]
build-backend = "poetry.core.masonry.api"
requires = [ "hatchling>=1.24" ]
build-backend = "hatchling.build"
@@ -7,7 +7,7 @@ First, setup the environment with poetry:
> **_Note:_** This step is not needed if you are using the dev-container.
```shell
poetry install
uv sync
```
Then check the parameters that have been pre-configured in the `.env` file in this directory. (E.g. you might need to configure an `OPENAI_API_KEY` if you're using OpenAI as model provider).
@@ -15,13 +15,13 @@ Then check the parameters that have been pre-configured in the `.env` file in th
Second, generate the embeddings of the example document in the `./data` directory:
```shell
poetry run generate
uv run generate
```
Third, start app with `reflex` command:
```shell
poetry run reflex run
uv run reflex run
```
To deploy the application, refer to the Reflex deployment guide: https://reflex.dev/docs/hosting/deploy-quick-start/
@@ -1,7 +1,8 @@
import logging
import reflex as rx
from app.services.model import DEFAULT_MODEL
from app.services.extractor import ExtractorService, InvalidModelCode
from app.services.model import DEFAULT_MODEL
logger = logging.getLogger("uvicorn")
@@ -13,7 +14,7 @@ class StructuredQuery(rx.State):
code: str = DEFAULT_MODEL
error: str = None
@rx.background
@rx.event(background=True)
async def handle_query(self):
async with self:
if not self.query:
@@ -18,7 +18,7 @@ class MonacoComponent(rx.Component):
theme: rx.Var[str] = rx.color_mode_cond("light", "vs-dark") # type: ignore
# The width of the editor.
line: rx.Var[int] = rx.Var.create_safe(1, _var_is_string=False)
line: rx.Var[int] = rx.Var.create_safe(1)
# The height of the editor.
width: rx.Var[str]
@@ -1,23 +1,30 @@
[tool.poetry]
[project]
name = "app"
version = "0.1.0"
description = ""
authors = ["Marcus Schiesser <mail@marcusschiesser.de>"]
authors = [ { name = "Marcus Schiesser", email = "mail@marcusschiesser.de" } ]
readme = "README.md"
requires-python = ">=3.11,<4.0"
dependencies = [
"fastapi>=0.109.1",
"uvicorn>=0.23.2",
"python-dotenv>=1.0.0",
"pydantic<2.10",
"llama-index>=0.12.1",
"cachetools>=5.3.3",
"reflex>=0.6.2.post1",
]
[tool.poetry.scripts]
[project.scripts]
generate = "app.engine.generate:generate_datasource"
[tool.poetry.dependencies]
python = "^3.11,<4.0"
fastapi = "^0.109.1"
uvicorn = { extras = ["standard"], version = "^0.23.2" }
python-dotenv = "^1.0.0"
pydantic = "<2.10"
llama-index = "^0.12.1"
cachetools = "^5.3.3"
reflex = "^0.6.2.post1"
[project.optional-dependencies]
dev = [
"mypy>=1.8.0",
"pytest>=8.3.5",
"pytest-asyncio>=0.25.3",
]
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
requires = [ "hatchling>=1.24" ]
build-backend = "hatchling.build"
@@ -5,7 +5,7 @@ def generate_filters(doc_ids):
"""
Generate public/private document filters based on the doc_ids and the vector store.
"""
# public documents (ingested by "poetry run generate" or in the LlamaCloud UI) don't have the "private" field
# public documents (ingested by "uv run generate" or in the LlamaCloud UI) don't have the "private" field
public_doc_filter = MetadataFilter(
key="private",
value=None,
@@ -2,12 +2,12 @@ This is a [LlamaIndex](https://www.llamaindex.ai/) simple agentic RAG project us
## Getting Started
First, setup the environment with poetry:
First, setup the environment with uv:
> **_Note:_** This step is not needed if you are using the dev-container.
```shell
poetry install
uv sync
```
Then check the parameters that have been pre-configured in the `.env` file in this directory.
@@ -16,13 +16,13 @@ Make sure you have set the `OPENAI_API_KEY` for the LLM.
Second, generate the embeddings of the documents in the `./data` directory:
```shell
poetry run generate
uv run generate
```
Third, run the development server:
```shell
poetry run dev
uv run fastapi dev
```
Then open [http://localhost:8000](http://localhost:8000) with your browser to start the chat UI.
@@ -30,7 +30,7 @@ Then open [http://localhost:8000](http://localhost:8000) with your browser to st
To start the app optimized for **production**, run:
```
poetry run prod
uv run fastapi run
```
## Configure LLM and Embedding Model
@@ -12,7 +12,7 @@ def create_workflow(chat_request: Optional[ChatRequest] = None) -> AgentWorkflow
index = get_index(chat_request=chat_request)
if index is None:
raise RuntimeError(
"Index not found! Please run `poetry run generate` to index the data first."
"Index not found! Please run `uv run generate` to index the data first."
)
query_tool = get_query_engine_tool(index=index)
return AgentWorkflow.from_tools_or_functions(
@@ -2,12 +2,12 @@ This is a [LlamaIndex](https://www.llamaindex.ai/) multi-agents project using [W
## Getting Started
First, setup the environment with poetry:
First, setup the environment with uv:
> **_Note:_** This step is not needed if you are using the dev-container.
```shell
poetry install
uv sync
```
Then check the parameters that have been pre-configured in the `.env` file in this directory.
@@ -16,13 +16,13 @@ Make sure you have set the `OPENAI_API_KEY` for the LLM.
Second, generate the embeddings of the documents in the `./data` directory:
```shell
poetry run generate
uv run generate
```
Third, run the development server:
```shell
poetry run dev
uv run fastapi dev
```
Then open [http://localhost:8000](http://localhost:8000) with your browser to start the chat UI.
@@ -30,7 +30,7 @@ Then open [http://localhost:8000](http://localhost:8000) with your browser to st
To start the app optimized for **production**, run:
```
poetry run prod
uv run fastapi run
```
## Configure LLM and Embedding Model
@@ -56,7 +56,7 @@ To customize the UI, you can start by modifying the [./components/ui_event.jsx](
You can also generate a new code for the workflow using LLM by running the following command:
```
poetry run generate:ui
uv run generate_ui
```
## Learn More
@@ -2,12 +2,12 @@ This is a [LlamaIndex](https://www.llamaindex.ai/) multi-agents project using [W
## Getting Started
First, setup the environment with poetry:
First, setup the environment with uv:
> **_Note:_** This step is not needed if you are using the dev-container.
```shell
poetry install
uv sync
```
Then check the parameters that have been pre-configured in the `.env` file in this directory.
@@ -16,13 +16,13 @@ Make sure you have set the `OPENAI_API_KEY` for the LLM and the `E2B_API_KEY` fo
Second, generate the embeddings of the documents in the `./data` directory:
```shell
poetry run generate
uv run generate
```
Third, run the development server:
```shell
poetry run dev
uv run fastapi dev
```
Then open [http://localhost:8000](http://localhost:8000) with your browser to start the chat UI.
@@ -30,7 +30,7 @@ Then open [http://localhost:8000](http://localhost:8000) with your browser to st
To start the app optimized for **production**, run:
```
poetry run prod
uv run fastapi run
```
## Configure LLM and Embedding Model
@@ -1,6 +1,4 @@
import logging
import os
import subprocess
from app.settings import init_settings
from app.workflow import create_workflow
@@ -14,18 +12,15 @@ COMPONENT_DIR = "components"
def create_app():
env = os.environ.get("APP_ENV")
app = LlamaIndexServer(
workflow_factory=create_workflow, # A factory function that creates a new workflow for each request
ui_config=UIConfig(
component_dir=COMPONENT_DIR,
app_title="Chat App",
),
env=env,
logger=logger,
)
# You can also add custom routes to the app
# You can also add custom FastAPI routes to app
app.add_api_route("/api/health", lambda: {"message": "OK"}, status_code=200)
return app
@@ -33,14 +28,3 @@ def create_app():
load_dotenv()
init_settings()
app = create_app()
def run(env: str):
os.environ["APP_ENV"] = env
app_host = os.getenv("APP_HOST", "0.0.0.0")
app_port = os.getenv("APP_PORT", "8000")
if env == "dev":
subprocess.run(["fastapi", "dev", "--host", app_host, "--port", app_port])
else:
subprocess.run(["fastapi", "run", "--host", app_host, "--port", app_port])
@@ -1,35 +1,37 @@
[tool]
[tool.poetry]
[project]
name = "app"
version = "0.1.0"
description = ""
authors = ["Marcus Schiesser <mail@marcusschiesser.de>"]
authors = [
{ name = "Marcus Schiesser", email = "mail@marcusschiesser.de" }
]
readme = "README.md"
requires-python = ">=3.11,<3.14"
dependencies = [
"python-dotenv>=1.0.0,<2.0.0",
"pydantic<2.10",
"aiostream>=0.5.2,<0.6.0",
"llama-index-core>=0.12.28,<0.13.0",
"llama-index-server>=0.1.14,<0.2.0",
]
[tool.poetry.scripts]
"generate" = "generate:generate_index"
"generate:index" = "generate:generate_index"
"generate:ui" = "generate:generate_ui_for_workflow"
dev = "main:run('dev')"
prod = "main:run('prod')"
[project.optional-dependencies]
dev = [
"mypy>=1.8.0,<2.0.0",
"pytest>=8.3.5,<9.0.0",
"pytest-asyncio>=0.25.3,<0.26.0",
]
[tool.poetry.dependencies]
python = ">=3.11,<3.14"
python-dotenv = "^1.0.0"
pydantic = "<2.10"
aiostream = "^0.5.2"
llama-index-core = "^0.12.28"
llama-index-server = "^0.1.14"
[project.scripts]
generate = "generate:generate_index"
generate_index = "generate:generate_index"
generate_ui = "generate:generate_ui_for_workflow"
[tool.poetry.group.dev.dependencies]
mypy = "^1.8.0"
pytest = "^8.3.5"
pytest-asyncio = "^0.25.3"
[tool.mypy]
python_version = "3.11"
plugins = "pydantic.mypy"
exclude = ["tests", "venv", ".venv", "output", "config"]
exclude = [ "tests", "venv", ".venv", "output", "config" ]
check_untyped_defs = true
warn_unused_ignores = false
show_error_codes = true
@@ -38,12 +40,12 @@ ignore_missing_imports = true
follow_imports = "silent"
implicit_optional = true
strict_optional = false
disable_error_code = ["return-value", "assignment"]
disable_error_code = [ "return-value", "assignment" ]
[[tool.mypy.overrides]]
module = "app.*"
ignore_missing_imports = false
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
requires = [ "hatchling>=1.24" ]
build-backend = "hatchling.build"
+1 -1
View File
@@ -14,7 +14,7 @@ def get_query_engine(output_cls):
raise HTTPException(
status_code=500,
detail=str(
"StorageContext is empty - call 'poetry run generate' to generate the storage first"
"StorageContext is empty - call 'uv run generate' to generate the storage first"
),
)
+2 -2
View File
@@ -27,7 +27,7 @@ fly apps open
If you're having documents in the `./data` folder, run the following command to generate vector embeddings of the documents:
```
fly console --machine <machine_id> --command "poetry run generate"
fly console --machine <machine_id> --command "uv run generate"
```
Where `machine_id` is the ID of the machine where the app is running. You can show the running machines with the `fly machines` command.
@@ -67,7 +67,7 @@ docker run \
-v $(pwd)/data:/app/data \ # Use your local folder to read the data
-v $(pwd)/storage:/app/storage \ # Use your file system to store the vector database
<your_image_name> \
poetry run generate
uv run generate
```
The app will then be able to answer questions about the documents in the `./data` folder.
+10 -17
View File
@@ -13,38 +13,31 @@ RUN npm install && npm run build
# ====================================
# Backend
# ====================================
FROM python:3.11 AS build
FROM python:3.11 AS release
WORKDIR /app
ENV PYTHONPATH=/app
# Install Poetry
RUN curl -sSL https://install.python-poetry.org | POETRY_HOME=/opt/poetry python && \
cd /usr/local/bin && \
ln -s /opt/poetry/bin/poetry && \
poetry config virtualenvs.create false
# Install Astral uv
# Download the latest installer
ADD https://astral.sh/uv/install.sh /uv-installer.sh
# Install Chromium for web loader
# Can disable this if you don't use the web loader to reduce the image size
RUN apt update && apt install -y chromium chromium-driver
RUN sh /uv-installer.sh && rm /uv-installer.sh
# Install dependencies
COPY ./pyproject.toml ./poetry.lock* /app/
RUN poetry install --no-root --no-cache --only main
ENV PATH="/root/.local/bin/:$PATH"
# ====================================
# Release
# ====================================
FROM build AS release
COPY --from=frontend /app/frontend/out /app/static
COPY . .
# Install dependencies
RUN uv sync
# Remove frontend code
RUN rm -rf .frontend
EXPOSE 8000
CMD ["poetry", "run", "prod"]
CMD ["uv", "run", "fastapi", "run"]
@@ -7,8 +7,7 @@ First, setup the environment with poetry:
> **_Note:_** This step is not needed if you are using the dev-container.
```
poetry install
poetry shell
uv sync
```
Then check the parameters that have been pre-configured in the `.env` file in this directory. (E.g. you might need to configure an `OPENAI_API_KEY` if you're using OpenAI as model provider).
@@ -18,13 +17,13 @@ If you are using any tools or data sources, you can update their config files in
Second, generate the embeddings of the documents in the `./data` directory:
```
poetry run generate
uv run generate
```
Third, run the app:
```
poetry run dev
uv run dev
```
Open [http://localhost:8000](http://localhost:8000) with your browser to start the app.
@@ -55,7 +54,7 @@ You can start editing the API endpoints by modifying `app/api/routers/chat.py`.
To start the app optimized for **production**, run:
```
poetry run prod
uv run prod
```
## Deployments
@@ -1,33 +1,39 @@
[tool.poetry]
[project]
name = "app"
version = "0.1.0"
description = ""
authors = ["Marcus Schiesser <mail@marcusschiesser.de>"]
authors = [
{ name = "Marcus Schiesser", email = "mail@marcusschiesser.de" }
]
readme = "README.md"
requires-python = ">=3.11,<3.14"
dependencies = [
"llama-index>=0.12.1",
"fastapi[standard]>=0.109.1",
"uvicorn>=0.23.2",
"python-dotenv>=1.0.0",
"pydantic>=2.10",
"aiostream>=0.5.2",
"cachetools>=5.3.3",
"rich>=13.9.4",
]
[tool.poetry.scripts]
[project.optional-dependencies]
dev = [
"mypy>=1.8.0",
"pytest>=8.3.5",
"pytest-asyncio>=0.25.3",
]
[project.scripts]
generate = "app.engine.generate:generate_datasource"
dev = "run:dev" # Starts the app in dev mode
prod = "run:prod" # Starts the app in prod mode
build = "run:build" # Builds the frontend assets and copies them to the static directory
[tool.poetry.dependencies]
python = ">=3.11,<3.14"
fastapi = "^0.109.1"
uvicorn = { extras = ["standard"], version = "^0.23.2" }
python-dotenv = "^1.0.0"
pydantic = "<2.10"
aiostream = "^0.5.2"
cachetools = "^5.3.3"
llama-index = "^0.12.1"
rich = "^13.9.4"
[tool.poetry.group.dev.dependencies]
mypy = "^1.8.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
requires = [ "hatchling>=1.24" ]
build-backend = "hatchling.build"
[tool.mypy]
python_version = "3.11"
+11 -11
View File
@@ -65,7 +65,7 @@ def build():
rich.print(
"\n[bold]Built frontend successfully![/bold]"
"\n[bold]Run: 'poetry run prod' to start the app[/bold]"
"\n[bold]Run: 'uv run prod' to start the app[/bold]"
"\n[bold]Don't forget to update the .env file![/bold]"
)
except CalledProcessError as e:
@@ -205,16 +205,16 @@ async def _run_backend(
f"Port {APP_PORT} is not available! Please change the port in .env file or kill the process running on this port."
)
rich.print(f"\n[bold]Starting app on port {APP_PORT}...[/bold]")
poetry_executable = _get_poetry_executable()
uv_executable = _get_uv_executable()
process = await asyncio.create_subprocess_exec(
poetry_executable,
uv_executable,
"run",
"python",
"main.py",
env=envs,
)
# Wait for port is started
timeout = 30
timeout = 60
for _ in range(timeout):
await asyncio.sleep(1)
if process.returncode is not None:
@@ -265,21 +265,21 @@ def _get_node_package_manager() -> NodePackageManager:
)
def _get_poetry_executable() -> str:
def _get_uv_executable() -> str:
"""
Check for available Poetry executables and return the preferred one.
Returns 'poetry' if installed, falls back to 'poetry.cmd'.
Check for available UV executables and return the preferred one.
Returns 'uv' if installed, falls back to 'uv.cmd'.
Raises SystemError if neither is installed.
Returns:
str: The full path to the available Poetry executable
str: The full path to the available UV executable
"""
poetry_cmds = ["poetry", "poetry.cmd"]
for cmd in poetry_cmds:
uv_cmds = ["uv", "uv.cmd"]
for cmd in uv_cmds:
cmd_path = which(cmd)
if cmd_path is not None:
return cmd_path
raise SystemError("Poetry is not installed. Please install Poetry first.")
raise SystemError("uv is not installed. Please install uv first.")
def _is_port_available(port: int) -> bool: