Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
nadeesha committed Dec 10, 2024
1 parent fc06dfe commit 2ed1e8c
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 30 deletions.
29 changes: 17 additions & 12 deletions connectors/trpc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,19 @@ pnpm add @inferable/trpc-connector

## Quick Start

Create your tRPC router as normal:
Create your tRPC router with the Inferable plugin:

```ts
const t = initTRPC.create();
import { inferablePlugin } from "@inferable/trpc-connector";

const router = t.router;
const publicProcedure = t.procedure;
const t = initTRPC.create();
const withInferable = inferablePlugin();

const appRouter = t.router({
userById: publicProcedure
userById: t.procedure
.unstable_concat(withInferable) // It's safe to use unstable_concat - https://trpc.io/docs/faq#unstable
.input(z.object({ id: z.string() }))
.meta({ inferable: true }) // <--- Mark this procedure for Inferable
.meta({ description: "Fetch a user by their ID" }) // This will be used to encrich the LLM context
.query(({ input }) => {
return users.find((user) => user.id === input.id);
}),
Expand Down Expand Up @@ -83,16 +84,20 @@ const result = await client.run({
});
```

## Notes
## Technical Details

The plugin does two things:

1. It adds a `meta` field to the procedures with `{ inferable: { enabled: true } }`. This is used to identify the procedures that should be exposed as Inferable functions.
2. It adds a `ctx` field to the tRPC procedure so you can validate the context that's passed by Inferable in your down stream procedures or middleware.

- Preserve Middleware: All your existing tRPC middleware continues to work
- Type Safety: Maintains full type safety through your tRPC router
- Selective Exposure: Only procedures marked with meta({ inferable: true }) are exposed
- Custom Descriptions: Add descriptions to help guide the AI through meta({ description: "..." })
- This allows you model [human in the loop](https://docs.inferable.ai/pages/human-in-the-loop) workflows where you can fire off a approval request to a human before the function is run.
- It also allows you to handle [end-user authentication](https://docs.inferable.ai/pages/end-user-authentication) in your tRPC procedures.
- For more information on the context object, see the [context documentation](https://docs.inferable.ai/pages/context).

## Documentation

Inferable documentation contains all the information you need to get started with Inferable.
[Inferable documentation](https://docs.inferable.ai) contains all the information you need to get started with Inferable.

## Support

Expand Down
52 changes: 39 additions & 13 deletions connectors/trpc/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import { AnyRouter, initTRPC } from "@trpc/server";
import { Inferable } from "inferable";
import { RegisteredService } from "inferable/bin/types";
import { ContextInput, RegisteredService } from "inferable/bin/types";

type FunctionConfig = {
path: string;
description: string | undefined;
inputs: any | undefined;
fn: (input: unknown) => any;
fn: (input: unknown, ctx: ContextInput) => any;
};

type Procedure = {
_def?: {
meta?: {
description?: string;
inferable?: boolean;
inferable?: {
enabled: boolean;
additionalContext?: string;
};
};
inputs?: any;
};
Expand All @@ -26,6 +29,18 @@ function camelCase(str: string) {
.join("");
}

export function inferableTRPC() {
const t = initTRPC.context().meta().create();

return {
proc: t.procedure.meta({ inferable: true }).use(async (opts) => {
return opts.next({
ctx: opts.ctx as ContextInput,
});
}),
};
}

export function createInferableService({
name,
router,
Expand All @@ -43,24 +58,35 @@ export function createInferableService({
}): RegisteredService {
const fns: FunctionConfig[] = [];

const caller = createCaller(contextGetter?.() ?? {});

for (const [path, procedure] of Object.entries(router._def.procedures) as [
string,
Procedure
][]) {
if (procedure._def?.meta?.inferable) {
if (typeof caller[path] !== "function") {
throw new Error(
`Procedure ${path} is not a function. Got ${typeof caller[path]}`
);
}

fns.push({
path,
description: procedure._def?.meta?.description,
description:
[
procedure._def?.meta?.description,
procedure._def?.meta?.inferable?.additionalContext,
]
.filter(Boolean)
.join("\n") || undefined,
inputs: procedure._def?.inputs,
fn: caller[path],
fn: async (input: unknown, ctx: ContextInput) => {
const context = contextGetter ? await contextGetter() : {};
const caller = createCaller({ ...ctx, context });

if (typeof caller[path] !== "function") {
throw new Error(
`Procedure ${path} is not a function. Got ${typeof caller[path]}`
);
}

const fn = caller[path] as (input: unknown) => any;

return fn(input);
},
});
}
}
Expand Down
12 changes: 7 additions & 5 deletions connectors/trpc/src/test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { initTRPC } from "@trpc/server";
import { createHTTPServer } from "@trpc/server/adapters/standalone";
import { z } from "zod";
import { createInferableService } from ".";
import { Inferable } from "inferable";
import { createInferableService, inferableTRPC } from ".";
import { ContextInput, Inferable } from "inferable";
import assert from "assert";

/**
Expand All @@ -23,21 +23,23 @@ const users = [
{ id: "2", name: "Jane Doe", email: "[email protected]" },
];

const plugin = inferableTRPC();

const appRouter = t.router({
"": publicProcedure.query(() => {
return `Inferable TRPC Connector Test v${
require("../package.json").version
}`;
}),
userById: publicProcedure
.unstable_concat(plugin.proc)
.input(z.object({ id: z.string() }))
.meta({ inferable: true })
.query(({ input }) => {
.query(({ input, ctx }) => {
return users.find((user) => user.id === input.id);
}),
users: router({
create: publicProcedure
.meta({ description: "Create a new user", inferable: true })
.unstable_concat(plugin.proc)
.input(z.object({ name: z.string(), email: z.string() }))
.mutation(({ input }) => {
const newUser = { id: (users.length + 1).toString(), ...input };
Expand Down

0 comments on commit 2ed1e8c

Please sign in to comment.