langsmith-cai
Version:
Client library to connect to the LangSmith LLM Tracing and Evaluation Platform.
263 lines (262 loc) • 10.9 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const child_process = __importStar(require("child_process"));
const path = __importStar(require("path"));
const util = __importStar(require("util"));
const env_js_1 = require("../utils/env.cjs");
const commander_1 = require("commander");
const child_process_1 = require("child_process");
const currentFileName = __filename;
const program = new commander_1.Command();
async function getDockerComposeCommand() {
const exec = util.promisify(child_process.exec);
try {
await exec("docker compose --version");
return ["docker", "compose"];
}
catch {
try {
await exec("docker-compose --version");
return ["docker-compose"];
}
catch {
throw new Error("Neither 'docker compose' nor 'docker-compose' commands are available. Please install the Docker server following the instructions for your operating system at https://docs.docker.com/engine/install/");
}
}
}
async function pprintServices(servicesStatus) {
const services = [];
for (const service of servicesStatus) {
const serviceStatus = {
Service: String(service["Service"]),
Status: String(service["Status"]),
};
const publishers = service["Publishers"] || [];
if (publishers) {
serviceStatus["PublishedPorts"] = publishers
.map((publisher) => String(publisher["PublishedPort"]))
.join(", ");
}
services.push(serviceStatus);
}
const maxServiceLen = Math.max(...services.map((service) => service["Service"].length));
const maxStateLen = Math.max(...services.map((service) => service["Status"].length));
const serviceMessage = [
"\n" +
"Service".padEnd(maxServiceLen + 2) +
"Status".padEnd(maxStateLen + 2) +
"Published Ports",
];
for (const service of services) {
const serviceStr = service["Service"].padEnd(maxServiceLen + 2);
const stateStr = service["Status"].padEnd(maxStateLen + 2);
const portsStr = service["PublishedPorts"] || "";
serviceMessage.push(serviceStr + stateStr + portsStr);
}
serviceMessage.push("\nTo connect, set the following environment variables" +
" in your LangChain application:" +
"\nLANGCHAIN_TRACING_V2=true" +
`\nLANGCHAIN_ENDPOINT=http://localhost:80/api`);
console.info(serviceMessage.join("\n"));
}
class SmithCommand {
constructor({ dockerComposeCommand }) {
Object.defineProperty(this, "dockerComposeCommand", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
Object.defineProperty(this, "dockerComposeFile", {
enumerable: true,
configurable: true,
writable: true,
value: ""
});
this.dockerComposeCommand = dockerComposeCommand;
this.dockerComposeFile = path.join(path.dirname(currentFileName), "docker-compose.yaml");
}
async executeCommand(command) {
return new Promise((resolve, reject) => {
const child = (0, child_process_1.spawn)(command[0], command.slice(1), { stdio: "inherit" });
child.on("error", (error) => {
console.error(`error: ${error.message}`);
reject(error);
});
child.on("close", (code) => {
if (code !== 0) {
reject(new Error(`Process exited with code ${code}`));
}
else {
resolve();
}
});
});
}
static async create() {
console.info("BY USING THIS SOFTWARE YOU AGREE TO THE TERMS OF SERVICE AT:");
console.info("https://smith.langchain.com/terms-of-service.pdf");
const dockerComposeCommand = await getDockerComposeCommand();
return new SmithCommand({ dockerComposeCommand });
}
async pull({ stage = "prod", version = "latest" }) {
if (stage === "dev") {
(0, env_js_1.setEnvironmentVariable)("_LANGSMITH_IMAGE_PREFIX", "dev-");
}
else if (stage === "beta") {
(0, env_js_1.setEnvironmentVariable)("_LANGSMITH_IMAGE_PREFIX", "rc-");
}
(0, env_js_1.setEnvironmentVariable)("_LANGSMITH_IMAGE_VERSION", version);
const command = [
...this.dockerComposeCommand,
"-f",
this.dockerComposeFile,
"pull",
];
await this.executeCommand(command);
}
async startLocal() {
const command = [
...this.dockerComposeCommand,
"-f",
this.dockerComposeFile,
];
command.push("up", "--quiet-pull", "--wait");
await this.executeCommand(command);
console.info("LangSmith server is running at http://localhost:1984.\n" +
"To view the app, navigate your browser to http://localhost:80" +
"\n\nTo connect your LangChain application to the server" +
" locally, set the following environment variable" +
" when running your LangChain application.");
console.info("\tLANGCHAIN_TRACING_V2=true");
console.info("\tLANGCHAIN_ENDPOINT=http://localhost:80/api");
}
async stop() {
const command = [
...this.dockerComposeCommand,
"-f",
this.dockerComposeFile,
"down",
];
await this.executeCommand(command);
}
async status() {
const command = [
...this.dockerComposeCommand,
"-f",
this.dockerComposeFile,
"ps",
"--format",
"json",
];
const exec = util.promisify(child_process.exec);
const result = await exec(command.join(" "));
const servicesStatus = JSON.parse(result.stdout);
if (servicesStatus) {
console.info("The LangSmith server is currently running.");
await pprintServices(servicesStatus);
}
else {
console.info("The LangSmith server is not running.");
}
}
async env() {
const env = await (0, env_js_1.getRuntimeEnvironment)();
const envVars = await (0, env_js_1.getLangChainEnvVars)();
const envDict = {
...env,
...envVars,
};
// Pretty print
const maxKeyLength = Math.max(...Object.keys(envDict).map((key) => key.length));
console.info("LangChain Environment:");
for (const [key, value] of Object.entries(envDict)) {
console.info(`${key.padEnd(maxKeyLength)}: ${value}`);
}
}
}
const startCommand = new commander_1.Command("start")
.description("Start the LangSmith server")
.option("--stage <stage>", "Which version of LangSmith to run. Options: prod, dev, beta (default: prod)")
.option("--openai-api-key <openaiApiKey>", "Your OpenAI API key. If not provided, the OpenAI API Key will be read" +
" from the OPENAI_API_KEY environment variable. If neither are provided," +
" some features of LangSmith will not be available.")
.option("--langsmith-license-key <langsmithLicenseKey>", "The LangSmith license key to use for LangSmith. If not provided, the LangSmith" +
" License Key will be read from the LANGSMITH_LICENSE_KEY environment variable." +
" If neither are provided, the Langsmith application will not spin up.")
.option("--version <version>", "The LangSmith version to use for LangSmith. Defaults to latest." +
" We recommend pegging this to the latest static version available at" +
" https://hub.docker.com/repository/docker/langchain/langchainplus-backend" +
" if you are using Langsmith in production.")
.action(async (args) => {
const smith = await SmithCommand.create();
if (args.openaiApiKey) {
(0, env_js_1.setEnvironmentVariable)("OPENAI_API_KEY", args.openaiApiKey);
}
if (args.langsmithLicenseKey) {
(0, env_js_1.setEnvironmentVariable)("LANGSMITH_LICENSE_KEY", args.langsmithLicenseKey);
}
await smith.pull({ stage: args.stage, version: args.version });
await smith.startLocal();
});
const stopCommand = new commander_1.Command("stop")
.description("Stop the LangSmith server")
.action(async () => {
const smith = await SmithCommand.create();
await smith.stop();
});
const pullCommand = new commander_1.Command("pull")
.description("Pull the latest version of the LangSmith server")
.option("--stage <stage>", "Which version of LangSmith to pull. Options: prod, dev, beta (default: prod)")
.option("--version <version>", "The LangSmith version to use for LangSmith. Defaults to latest." +
" We recommend pegging this to the latest static version available at" +
" https://hub.docker.com/repository/docker/langchain/langchainplus-backend" +
" if you are using Langsmith in production.")
.action(async (args) => {
const smith = await SmithCommand.create();
await smith.pull({ stage: args.stage, version: args.version });
});
const statusCommand = new commander_1.Command("status")
.description("Get the status of the LangSmith server")
.action(async () => {
const smith = await SmithCommand.create();
await smith.status();
});
const envCommand = new commander_1.Command("env")
.description("Get relevant environment information for the LangSmith server")
.action(async () => {
const smith = await SmithCommand.create();
await smith.env();
});
program
.description("Manage the LangSmith server")
.addCommand(startCommand)
.addCommand(stopCommand)
.addCommand(pullCommand)
.addCommand(statusCommand)
.addCommand(envCommand);
program.parse(process.argv);