Skip to content

Commit

Permalink
chore: Improve local development experience (#168)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnjcsmith authored Nov 30, 2024
1 parent fed85c6 commit 05b68a6
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 39 deletions.
21 changes: 21 additions & 0 deletions control-plane/.env.base
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
NODE_ENV="development"

APP_ORIGIN="http://localhost:3001"

DATABASE_SSL_DISABLED="true"
DATABASE_URL="postgresql://inferable:inferable@localhost:5432/inferable"

REDIS_URL="redis://localhost:6379"

SQS_BASE_QUEUE_URL='http://localhost:9324/000000000000'
SQS_RUN_PROCESS_QUEUE_URL='http://localhost:9324/000000000000/run-process'
SQS_RUN_GENERATE_NAME_QUEUE_URL='http://localhost:9324/000000000000/run-generate-name'
SQS_CUSTOMER_TELEMETRY_QUEUE_URL='http://localhost:9324/000000000000/customer-telemetry'
SQS_EXTERNAL_TOOL_CALL_QUEUE_URL='http://localhost:9324/000000000000/external-tool-call'

# JWKS_URL=
# MANAGEMENT_API_SECRET=

# ANTHROPIC_API_KEY=
# COHERE_API_KEY=
# BEDROCK_AVAILABLE=
1 change: 1 addition & 0 deletions control-plane/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ ENV SHORT_VERSION=$SHORT_VERSION
COPY --from=build /build/ /app

ENV NODE_ENV=production
ENV ENVIRONMENT=prod

EXPOSE 4000
CMD [ "npm", "run", "start" ]
58 changes: 58 additions & 0 deletions control-plane/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,64 @@ Inferable's control-plane is open-source and self-hostable.

[Self hosting guide](https://docs.inferable.ai/pages/self-hosting)

### Local Development

To run the control plane locally for development:

1. Start the local resources required for development:
```bash
docker compose -f docker-compose.dev.yml up
```

This will start:
- PostgreSQL database with pgvector
- Redis for caching
- ElasticMQ for a local SQS-compatible queue implementation

2. Populate environment variables:

Development environment varaiables are managed in the `.env`.

`.env.base` contains a base set of required environment variables. Copy `.env.base` to `.env`.

```base
cp .env.base .env
```

You will need to populate the following environment variables in `.env`:

- Model provider API keys (`ANTHROPIC_API_KEY` and `COHERE_API_KEY`) OR `BEDROCK_AVAILABLE`
- If you specify `BEDROCK_AVAILABLE` ensure your environment has access to AWS Bedrock. (See [routing.ts](https://github.com/inferablehq/inferable/blob/main/control-plane/src/modules/models/routing.ts) for model requirements)
- `JWKS_URL` OR `MANAGEMENT_API_SECRET` (For headless mode)

4. Run DB migrations:

Inferable uses [drizzle](https://github.com/drizzle-team/drizzle-orm) to manage database migrations. To run migrations:

```bash
npm run migrate
```

3. Start the control plane:
```bash
npm run dev
```

The API will be available at `http://localhost:4000`.

4. Connect via the CLI (Optional):

```bash
npm install -g @inferable/cli
export INFERABLE_API_ENDPOINT=http://localhost:4000

# If running in headless mode, you will be prompted for the management API secret
inf auth login

# Create a new cluster
inf clusters create
```

## Documentation

- [Inferable documentation](https://docs.inferable.ai/) contains all the information you need to get started with Inferable.
Expand Down
8 changes: 5 additions & 3 deletions control-plane/docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ services:
image: pgvector/pgvector:pg16
container_name: postgres
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydb
POSTGRES_USER: inferable
POSTGRES_PASSWORD: inferable
POSTGRES_DB: inferable
ports:
- "5432:5432"
volumes:
Expand Down Expand Up @@ -43,4 +43,6 @@ configs:
queues {
run-process {}
run-generate-name {}
customer-telemetry {}
external-tool-call {}
}
18 changes: 9 additions & 9 deletions control-plane/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,16 @@ app.addHook("onRequest", (request, _reply, done) => {
const startTime = Date.now();

(async function start() {
logger.info("Starting server");
logger.info("Starting server", {
environment: env.ENVIRONMENT,
ee: env.EE_DEPLOYMENT,
headless: !!env.MANAGEMENT_API_SECRET
});

if (env.ENVIRONMENT === "prod") {
await runMigrations().then(() => {
logger.info("Database migrated", { latency: Date.now() - startTime });
});
}
await runMigrations()

if (!env.EE_DEPLOYMENT) {
logger.info("Running in hobby mode");
logger.info("Database migrated", { latency: Date.now() - startTime });
}

await Promise.all([
Expand All @@ -130,14 +130,14 @@ const startTime = Date.now();
workflows.start(),
knowledge.start(),
models.start(),
redis.start(),
...(env.EE_DEPLOYMENT
? [
flagsmith?.getEnvironmentFlags(),
analytics.start(),
customerTelemetry.start(),
analytics.start(),
toolhouse.start(),
externalCalls.start(),
redis.start(),
]
: []),
])
Expand Down
16 changes: 8 additions & 8 deletions control-plane/src/modules/auth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,17 +113,17 @@ export const plugin = fastifyPlugin(async (fastify: FastifyInstance) => {
export const extractAuthState = async (
token: string,
): Promise<Auth | undefined> => {
// Master Secret support (Hobby deployments only)
if (token && token === env.MASTER_API_SECRET) {
// Management Secret support (Hobby deployments only)
if (token && token === env.MANAGEMENT_API_SECRET) {
// This is also validated on startup
if (env.EE_DEPLOYMENT) {
throw new Error("Can not use master secret in EE deployment");
throw new Error("Can not use management secret in EE deployment");
}

return {
type: "api",
entityId: "MASTER_API_SECRET",
organizationId: "MASTER",
entityId: "MANAGEMENT_API_SECRET",
organizationId: "ROOT",
canAccess: async function () {
return this;
},
Expand All @@ -134,14 +134,14 @@ export const extractAuthState = async (
return this;
},
isMachine: function () {
throw new AuthenticationError("Master API secret auth is not machine");
throw new AuthenticationError("Management API secret auth is not machine");
},
isClerk: function () {
throw new AuthenticationError("Master API secret auth is not clerk");
throw new AuthenticationError("Management API secret auth is not clerk");
},
isCustomerProvided: function () {
throw new AuthenticationError(
"Master API secret auth is not customer provided",
"Management API secret auth is not customer provided",
);
},
isAdmin: function () {
Expand Down
43 changes: 24 additions & 19 deletions control-plane/src/utilities/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const envSchema = z
LOG_LEVEL: z.enum(["error", "warn", "info", "debug"]).default("info"),
ENABLE_FASTIFY_LOGGER: truthy.default(false),

MASTER_API_SECRET: z.string().optional(),
MANAGEMENT_API_SECRET: z.string().optional(),

DATABASE_URL: z.string().url(),
DATABASE_SSL_DISABLED: truthy.default(false),
Expand All @@ -36,25 +36,24 @@ const envSchema = z

JOB_LONG_POLLING_TIMEOUT: z.number().default(15),

REDIS_URL: z.string().url(),

ANTHROPIC_API_KEY: z.string().optional(),
COHERE_API_KEY: z.string().optional(),

SQS_RUN_PROCESS_QUEUE_URL: z.string(),
SQS_RUN_GENERATE_NAME_QUEUE_URL: z.string(),
SQS_LEARNING_INGEST_QUEUE_URL: z.string().optional(),
SQS_CUSTOMER_TELEMETRY_QUEUE_URL: z.string(),
SQS_EXTERNAL_TOOL_CALL_QUEUE_URL: z.string(),

SQS_BASE_QUEUE_URL: z.string().optional(),

// Required in EE (Disabled by default)
EE_DEPLOYMENT: truthy.default(false),

SQS_LEARNING_INGEST_QUEUE_URL: z.string().optional(),
SQS_CUSTOMER_TELEMETRY_QUEUE_URL: z.string().optional(),
SQS_EXTERNAL_TOOL_CALL_QUEUE_URL: z.string().optional(),

APP_ORIGIN: z.string().url().optional(),

REDIS_URL: z.string().url().optional(),

JWKS_URL: z.string().url().optional(),
JWT_IGNORE_EXPIRATION: truthy.default(false),

Expand All @@ -71,15 +70,29 @@ const envSchema = z
ANALYTICS_BUCKET_NAME: z.string().optional(),
})
.superRefine((value, ctx) => {
if (!value.MANAGEMENT_API_SECRET && !value.JWKS_URL) {
return ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "MANAGEMENT_API_SECRET or JWKS_URL is required",
path: ["MANAGEMENT_API_SECRET", "JWKS_URL"],
});
}

if (value.MANAGEMENT_API_SECRET) {
if (value.JWKS_URL) {
return ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "MANAGEMENT_API_SECRET can not be set with JWKS_URL (Headless mode only)",
path: ["MANAGEMENT_API_SECRET"],
});
}
}

if (!value.EE_DEPLOYMENT) {
return;
}
const EE_REQUIRED = [
"SQS_LEARNING_INGEST_QUEUE_URL",
"SQS_CUSTOMER_TELEMETRY_QUEUE_URL",
"SQS_EXTERNAL_TOOL_CALL_QUEUE_URL",
"APP_ORIGIN",
"REDIS_URL",
"JWKS_URL",
"HYPERDX_API_KEY",
"ROLLBAR_ACCESS_TOKEN",
Expand All @@ -89,14 +102,6 @@ const envSchema = z
"ANALYTICS_BUCKET_NAME",
];

if (value.MASTER_API_SECRET) {
return ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "MASTER_API_SECRET can not be set for EE Deployment",
path: ["MASTER_API_SECRET"],
});
}

for (const key of EE_REQUIRED) {
//eslint-disable-next-line @typescript-eslint/no-explicit-any
if (!(value as any)[key]) {
Expand Down

0 comments on commit 05b68a6

Please sign in to comment.