Compare commits
2 Commits
05ebf84bb9
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| ff5450a9b0 | |||
| 9941d61fb8 |
132
README.md
132
README.md
@@ -1,10 +1,10 @@
|
||||
# JLINC Langchain Integration
|
||||
|
||||
The JLINC Langchain Integration is the official way to implement the zero-knowledge third-party auditing provided by the [JLINC Server](https://gitea.jlinc.io/jlinc-labs/jlinc-server) inside any Langchain-based infrastructure.
|
||||
The JLINC Langchain Integration is the official way to implement the zero-knowledge third-party auditing and authorization provided by the [JLINC Server](https://gitea.jlinc.io/jlinc-labs/jlinc-server) inside any Langchain-based infrastructure.
|
||||
|
||||
By embedding JLINC's trusted protocol directly into Langchain's tracing system, organizations can prove compliance, accountability, and data integrity without ever exposing sensitive information. This seamless integration enables developers to track, verify, and audit model interactions with full transparency while preserving confidentiality through cryptographically verifiable zero-knowledge proofs. Whether for regulated industries, enterprise governance, or AI safety applications, the JLINC Langchain Tracer ensures that trust, privacy, and accountability are built in from the ground up.
|
||||
|
||||
## Sample application
|
||||
## Sample auditing application
|
||||
The below code sample is a demonstration of the JLINC Langchain Integration in action. As data moves through the chain, it is cryptographically signed with a unique key for each element in the chain, and zero-knowledge audit records are delivered to the JLINC Archive Server.
|
||||
|
||||
```javascript
|
||||
@@ -16,7 +16,7 @@ const { ChatPromptTemplate } = require("@langchain/core/prompts");
|
||||
const { JLINCTracer } = require("@jlinc/langchain");
|
||||
|
||||
async function main() {
|
||||
const tracer = new JLINCTracer({
|
||||
const config = {
|
||||
dataStoreApiUrl: "http://localhost:9090",
|
||||
dataStoreApiKey: process.env.JLINC_DATA_STORE_API_KEY,
|
||||
archiveApiUrl: "http://localhost:9090",
|
||||
@@ -24,7 +24,9 @@ async function main() {
|
||||
agreementId: "00000000-0000-0000-0000-000000000000",
|
||||
systemPrefix: "TracerTest",
|
||||
debug: true,
|
||||
});
|
||||
}
|
||||
|
||||
const tracer = new JLINCTracer(config);
|
||||
|
||||
const llm = new ChatOpenAI({
|
||||
openAIApiKey: "n/a",
|
||||
@@ -66,6 +68,128 @@ async function main() {
|
||||
main()
|
||||
```
|
||||
|
||||
## Sample authorization code
|
||||
The JLINC integration also supports [AuthZEN](https://openid.net/wg/authzen/)-styled authorization pass through to any provider. For instance, the below modifications to the above code would add in authorization to determine if a "public" or "private" tool or LLM can be utilized:
|
||||
|
||||
```javascript
|
||||
const { ChatOpenAI } = require("@langchain/openai");
|
||||
const { awaitAllCallbacks } = require("@langchain/core/callbacks/promises");
|
||||
const { Calculator } = require("@langchain/community/tools/calculator");
|
||||
const { AgentExecutor, createToolCallingAgent } = require("langchain/agents");
|
||||
const { ChatPromptTemplate } = require("@langchain/core/prompts");
|
||||
const { JLINCTracer, JLINCAuthDecision, JLINCAuthBaseChatModel, JLINCAuthTool } = require("../src/index.js");
|
||||
|
||||
class CalculatorPrivate extends Calculator {
|
||||
static lc_name() {
|
||||
return "CalculatorPrivate";
|
||||
}
|
||||
}
|
||||
|
||||
class CalculatorPublic extends Calculator {
|
||||
static lc_name() {
|
||||
return "CalculatorPublic";
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const config = {
|
||||
dataStoreApiUrl: "http://localhost:9090",
|
||||
dataStoreApiKey: process.env.JLINC_DATA_STORE_API_KEY,
|
||||
archiveApiUrl: "http://localhost:9090",
|
||||
archiveApiKey: process.env.JLINC_ARCHIVE_API_KEY,
|
||||
agreementId: "00000000-0000-0000-0000-000000000000",
|
||||
systemPrefix: "TestTracerJlinc",
|
||||
debug: true,
|
||||
}
|
||||
|
||||
const jlincAuthDecision = new JLINCAuthDecision(config);
|
||||
const auth = {
|
||||
subject: {
|
||||
type: "user",
|
||||
id: "tester",
|
||||
},
|
||||
action: {
|
||||
name: "read",
|
||||
},
|
||||
resource: {
|
||||
type: "data",
|
||||
id: "1234",
|
||||
properties: {
|
||||
ownerID: "tester@test.com",
|
||||
}
|
||||
}
|
||||
}
|
||||
await jlincAuthDecision.evaluate(auth);
|
||||
|
||||
const tracer = new JLINCTracer(config);
|
||||
|
||||
const authorizedLlm = new ChatOpenAI({
|
||||
openAIApiKey: "n/a",
|
||||
configuration: {
|
||||
baseURL: "http://localhost:1234/v1",
|
||||
},
|
||||
modelName: "meta-llama-3.1-8b-instruct",
|
||||
});
|
||||
const notAuthorizedLlm = new ChatOpenAI({
|
||||
openAIApiKey: "n/a",
|
||||
configuration: {
|
||||
baseURL: "http://localhost:1234/v1",
|
||||
},
|
||||
modelName: "hermes-3-llama-3.1-8b",
|
||||
});
|
||||
const llm = new JLINCAuthBaseChatModel({
|
||||
config,
|
||||
jlincAuthDecision,
|
||||
targetAuthorized: authorizedLlm,
|
||||
targetNotAuthorized: notAuthorizedLlm, // Optional
|
||||
});
|
||||
|
||||
const calculatorPublic = new CalculatorPublic();
|
||||
calculatorPublic.name = 'calculator_public';
|
||||
const calculatorPrivate = new CalculatorPrivate();
|
||||
calculatorPrivate.name = 'calculator_private';
|
||||
const jlincAuthTool = new JLINCAuthTool({
|
||||
config,
|
||||
jlincAuthDecision,
|
||||
targetAuthorized: calculatorPublic,
|
||||
targetNotAuthorized: calculatorPrivate, // Optional
|
||||
});
|
||||
const tools = [jlincAuthTool];
|
||||
|
||||
const prompt = ChatPromptTemplate.fromMessages([
|
||||
["system", "You are a helpful assistant"],
|
||||
["placeholder", "{chat_history}"],
|
||||
["human", "{input}"],
|
||||
["placeholder", "{agent_scratchpad}"],
|
||||
]);
|
||||
|
||||
const agent = createToolCallingAgent({ llm, tools, prompt });
|
||||
|
||||
const agentExecutor = new AgentExecutor({
|
||||
agent,
|
||||
tools,
|
||||
});
|
||||
|
||||
try {
|
||||
const r = await agentExecutor.invoke({ input: "Add 1 + 1. If a function call is used, tell me the output of the function call." }, { callbacks: [tracer] });
|
||||
|
||||
// The next invocation requires a reauth for any future calls to the agent:
|
||||
auth.action.name = "write";
|
||||
jlincAuthDecision.evaluate(auth);
|
||||
|
||||
console.log(`\nResult`)
|
||||
console.log(`---------------------------------------------`)
|
||||
console.log(r)
|
||||
} catch (err) {
|
||||
console.error("Error calling LLM:", err);
|
||||
} finally {
|
||||
await awaitAllCallbacks();
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
```
|
||||
|
||||
|
||||
## Additional information
|
||||
|
||||
|
||||
51
dist/auth/JLINCAuthBaseChatModel.d.ts
vendored
Normal file
51
dist/auth/JLINCAuthBaseChatModel.d.ts
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
export type JLINCConfig = import("./common").JLINCConfig;
|
||||
export type JLINCAuthBaseChatModelFields = {
|
||||
config: JLINCConfig;
|
||||
jlincAuthDecision: Tool;
|
||||
targetAuthorized: BaseChatModel;
|
||||
targetNotAuthorized: BaseChatModel | null;
|
||||
};
|
||||
/**
|
||||
* @typedef {import('./common').JLINCConfig} JLINCConfig
|
||||
*/
|
||||
/**
|
||||
* @typedef {Object} JLINCAuthBaseChatModelFields
|
||||
* @property {JLINCConfig} config
|
||||
* @property {Tool} jlincAuthDecision
|
||||
* @property {BaseChatModel} targetAuthorized
|
||||
* @property {BaseChatModel|null} targetNotAuthorized
|
||||
*/
|
||||
export class JLINCAuthBaseChatModel extends BaseChatModel<import("@langchain/core/language_models/chat_models").BaseChatModelCallOptions, import("@langchain/core/dist/messages/ai.js").AIMessageChunk> {
|
||||
/**
|
||||
* @param {JLINCAuthBaseChatModelFields} fields
|
||||
*/
|
||||
constructor({ config, jlincAuthDecision, targetAuthorized, targetNotAuthorized }: JLINCAuthBaseChatModelFields);
|
||||
/** @type {JLINCConfig} */
|
||||
config: JLINCConfig;
|
||||
/** @type {Tool} */
|
||||
jlincAuthDecision: Tool;
|
||||
/** @type {BaseChatModel} */
|
||||
targetAuthorized: BaseChatModel;
|
||||
/** @type {BaseChatModel|null} */
|
||||
targetNotAuthorized: BaseChatModel | null;
|
||||
/** @type {JLINCTracer} */
|
||||
tracer: JLINCTracer;
|
||||
/** @type {string} */
|
||||
authType: string;
|
||||
/**
|
||||
* @param {BindToolsInput[]} tools - The tools to bind to the underlying models.
|
||||
* @param {Partial<CallOptions>} [kwargs] - Optional call options.
|
||||
* @returns {Runnable<BaseLanguageModelInput, OutputMessageType, CallOptions>}
|
||||
* A new JLINCAuthBaseChatModel instance whose underlying models
|
||||
* have been bound with the provided tools.
|
||||
*/
|
||||
bindTools(tools: BindToolsInput[], kwargs?: Partial<CallOptions>): Runnable<BaseLanguageModelInput, OutputMessageType, CallOptions>;
|
||||
/**
|
||||
* @param {BaseLanguageModelInput} input
|
||||
* @param {Partial<CallOptions>} [options] - Optional call options.
|
||||
* @returns {Promise<OutputMessageType>} A promise that resolves with the output.
|
||||
*/
|
||||
invoke(input: BaseLanguageModelInput, runManager: any): Promise<OutputMessageType>;
|
||||
}
|
||||
import { BaseChatModel } from "@langchain/core/dist/language_models/chat_models.js";
|
||||
import { JLINCTracer } from "../tracer/index.js";
|
||||
96
dist/auth/JLINCAuthBaseChatModel.js
vendored
Normal file
96
dist/auth/JLINCAuthBaseChatModel.js
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
const { BaseChatModel } = require("@langchain/core/language_models/chat_models");
|
||||
const { JLINCTracer } = require("../tracer/index.js");
|
||||
const { authDecide, authInvoke, getDescription, getName } = require("./common.js");
|
||||
|
||||
/**
|
||||
* @typedef {import('./common').JLINCConfig} JLINCConfig
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} JLINCAuthBaseChatModelFields
|
||||
* @property {JLINCConfig} config
|
||||
* @property {Tool} jlincAuthDecision
|
||||
* @property {BaseChatModel} targetAuthorized
|
||||
* @property {BaseChatModel|null} targetNotAuthorized
|
||||
*/
|
||||
|
||||
class JLINCAuthBaseChatModel extends BaseChatModel {
|
||||
/**
|
||||
* @param {JLINCAuthBaseChatModelFields} fields
|
||||
*/
|
||||
constructor({ config, jlincAuthDecision, targetAuthorized, targetNotAuthorized }) {
|
||||
super({
|
||||
name: "jlinc_auth_base_chat_model",
|
||||
description: "", // overridden dynamically
|
||||
});
|
||||
Object.defineProperty(this, "name", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: getName(targetAuthorized, targetNotAuthorized)
|
||||
});
|
||||
Object.defineProperty(this, "description", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: getDescription(targetAuthorized, targetNotAuthorized, 'BaseChatModel')
|
||||
});
|
||||
/** @type {JLINCConfig} */
|
||||
this.config = config;
|
||||
/** @type {Tool} */
|
||||
this.jlincAuthDecision = jlincAuthDecision;
|
||||
/** @type {BaseChatModel} */
|
||||
this.targetAuthorized = targetAuthorized;
|
||||
/** @type {BaseChatModel|null} */
|
||||
this.targetNotAuthorized = targetNotAuthorized;
|
||||
/** @type {JLINCTracer} */
|
||||
this.tracer = new JLINCTracer(config);
|
||||
/** @type {string} */
|
||||
this.authType = 'BaseChatModel';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BindToolsInput[]} tools - The tools to bind to the underlying models.
|
||||
* @param {Partial<CallOptions>} [kwargs] - Optional call options.
|
||||
* @returns {Runnable<BaseLanguageModelInput, OutputMessageType, CallOptions>}
|
||||
* A new JLINCAuthBaseChatModel instance whose underlying models
|
||||
* have been bound with the provided tools.
|
||||
*/
|
||||
bindTools(tools, kwargs = {}) {
|
||||
if (this.config.debug)
|
||||
console.log(`[JLINCAuth] Binding tools to BaseChatModels`);
|
||||
const authorizedBound = this.targetAuthorized.bindTools(tools, kwargs);
|
||||
const notAuthorizedBound = this.targetNotAuthorized
|
||||
? this.targetNotAuthorized.bindTools(tools, kwargs)
|
||||
: null;
|
||||
return new JLINCAuthBaseChatModel({
|
||||
config: this.config,
|
||||
jlincAuthDecision: this.jlincAuthDecision,
|
||||
targetAuthorized: authorizedBound,
|
||||
targetNotAuthorized: notAuthorizedBound,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} - type
|
||||
*/
|
||||
_llmType() {
|
||||
const type = this.targetAuthorized?._llmType?.() ?? "unknown";
|
||||
if (this.config?.debug)
|
||||
console.log(`[JLINCAuth] Returning LLM type: ${type}`);
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BaseLanguageModelInput} input
|
||||
* @param {Partial<CallOptions>} [options] - Optional call options.
|
||||
* @returns {Promise<OutputMessageType>} A promise that resolves with the output.
|
||||
*/
|
||||
async invoke(input, runManager) {
|
||||
return await authInvoke(this, input);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
JLINCAuthBaseChatModel,
|
||||
};
|
||||
29
dist/auth/JLINCAuthDecision.d.ts
vendored
Normal file
29
dist/auth/JLINCAuthDecision.d.ts
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
export type JLINCConfig = import("./common").JLINCConfig;
|
||||
/**
|
||||
* @typedef {import('./common').JLINCConfig} JLINCConfig
|
||||
*/
|
||||
export class JLINCAuthDecision extends Tool<any> {
|
||||
/**
|
||||
* @param {JLINCConfig} fields
|
||||
*/
|
||||
constructor(config: any);
|
||||
/** @type {JLINCConfig} */
|
||||
config: JLINCConfig;
|
||||
/** @type {Boolean} */
|
||||
authenticated: boolean;
|
||||
/**
|
||||
* @param {any} payload
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
postToApi(auth: any): Promise<any>;
|
||||
/**
|
||||
* @param {auth} auth
|
||||
* @returns {Promise<boolean>} - authenticated
|
||||
*/
|
||||
evaluate(auth: any): Promise<boolean>;
|
||||
/**
|
||||
* @returns {boolean} - authenticated
|
||||
*/
|
||||
getAuth(): boolean;
|
||||
}
|
||||
import { Tool } from "@langchain/core/dist/tools";
|
||||
79
dist/auth/JLINCAuthDecision.js
vendored
Normal file
79
dist/auth/JLINCAuthDecision.js
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
const { Tool } = require("@langchain/core/tools");
|
||||
const axios = require("axios");
|
||||
|
||||
/**
|
||||
* @typedef {import('./common').JLINCConfig} JLINCConfig
|
||||
*/
|
||||
|
||||
class JLINCAuthDecision extends Tool {
|
||||
/**
|
||||
* @param {JLINCConfig} fields
|
||||
*/
|
||||
constructor(config) {
|
||||
super({
|
||||
name: "jlinc_auth",
|
||||
description: "Authorization via AuthZEN format.",
|
||||
});
|
||||
Object.defineProperty(this, "name", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: "jlinc-auth"
|
||||
});
|
||||
Object.defineProperty(this, "description", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: "Authorization via AuthZEN format."
|
||||
});
|
||||
/** @type {JLINCConfig} */
|
||||
this.config = config;
|
||||
/** @type {Boolean} */
|
||||
this.authenticated = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} payload
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
async postToApi(auth) {
|
||||
const response = await axios.post(
|
||||
`${this.config.dataStoreApiUrl}/api/v1/auth`,
|
||||
auth,
|
||||
{
|
||||
headers: {
|
||||
'Authorization': `Bearer ${this.config.dataStoreApiKey}`,
|
||||
}
|
||||
}
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {auth} auth
|
||||
* @returns {Promise<boolean>} - authenticated
|
||||
*/
|
||||
async evaluate(auth) {
|
||||
this.authenticated = false;
|
||||
try {
|
||||
this.authenticated = (await this.postToApi(auth)).decision;
|
||||
} catch (e) {
|
||||
console.log(`[JLINCAuthTool] Error connecting to API, flagging as unauthorized`);
|
||||
if (this.config.debug) console.error(e)
|
||||
}
|
||||
if (this.config.debug)
|
||||
console.log(`[JLINCAuth] Got authorization of: ${this.authenticated}`);
|
||||
return this.authenticated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean} - authenticated
|
||||
*/
|
||||
getAuth() {
|
||||
return this.authenticated;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
JLINCAuthDecision,
|
||||
};
|
||||
47
dist/auth/JLINCAuthTool.d.ts
vendored
Normal file
47
dist/auth/JLINCAuthTool.d.ts
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
export type JLINCConfig = import("./common").JLINCConfig;
|
||||
export type JLINCAuthToolFields = {
|
||||
config: JLINCConfig;
|
||||
jlincAuthDecision: Tool;
|
||||
targetAuthorized: Tool;
|
||||
targetNotAuthorized: Tool | null;
|
||||
};
|
||||
/**
|
||||
* @typedef {import('./common').JLINCConfig} JLINCConfig
|
||||
*/
|
||||
/**
|
||||
* @typedef {Object} JLINCAuthToolFields
|
||||
* @property {JLINCConfig} config
|
||||
* @property {Tool} jlincAuthDecision
|
||||
* @property {Tool} targetAuthorized
|
||||
* @property {Tool|null} targetNotAuthorized
|
||||
*/
|
||||
export class JLINCAuthTool extends Tool<any> {
|
||||
/**
|
||||
* @param {JLINCAuthToolFields} fields
|
||||
*/
|
||||
constructor({ config, jlincAuthDecision, targetAuthorized, targetNotAuthorized }: JLINCAuthToolFields);
|
||||
/** @type {JLINCConfig} */
|
||||
config: JLINCConfig;
|
||||
/** @type {Tool} */
|
||||
jlincAuthDecision: Tool;
|
||||
/** @type {Tool} */
|
||||
targetAuthorized: Tool;
|
||||
/** @type {Tool|null} */
|
||||
targetNotAuthorized: Tool | null;
|
||||
/** @type {JLINCTracer} */
|
||||
tracer: JLINCTracer;
|
||||
/** @type {string} */
|
||||
authType: string;
|
||||
/**
|
||||
* @template TInput
|
||||
* @template {undefined | ToolRunnableConfig} TConfig
|
||||
* @template TOutput
|
||||
*
|
||||
* @param {TInput} input
|
||||
* @param {TConfig} [config]
|
||||
* @returns {Promise<ToolReturnType<TInput, TConfig, TOutput>>}
|
||||
*/
|
||||
invoke<TInput, TConfig extends undefined | ToolRunnableConfig, TOutput>(input: TInput, runManager: any): Promise<ToolReturnType<TInput, TConfig, TOutput>>;
|
||||
}
|
||||
import { Tool } from "@langchain/core/dist/tools/index.js";
|
||||
import { JLINCTracer } from "../tracer/index.js";
|
||||
68
dist/auth/JLINCAuthTool.js
vendored
Normal file
68
dist/auth/JLINCAuthTool.js
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
const { Tool } = require("@langchain/core/tools");
|
||||
const { JLINCTracer } = require("../tracer/index.js");
|
||||
const { authDecide, authInvoke, getDescription, getName } = require("./common.js");
|
||||
|
||||
/**
|
||||
* @typedef {import('./common').JLINCConfig} JLINCConfig
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} JLINCAuthToolFields
|
||||
* @property {JLINCConfig} config
|
||||
* @property {Tool} jlincAuthDecision
|
||||
* @property {Tool} targetAuthorized
|
||||
* @property {Tool|null} targetNotAuthorized
|
||||
*/
|
||||
|
||||
class JLINCAuthTool extends Tool {
|
||||
/**
|
||||
* @param {JLINCAuthToolFields} fields
|
||||
*/
|
||||
constructor({ config, jlincAuthDecision, targetAuthorized, targetNotAuthorized }) {
|
||||
super({
|
||||
name: "jlinc_auth_tool",
|
||||
description: "", // overridden dynamically
|
||||
});
|
||||
Object.defineProperty(this, "name", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: getName(targetAuthorized, targetNotAuthorized)
|
||||
});
|
||||
Object.defineProperty(this, "description", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: getDescription(targetAuthorized, targetNotAuthorized, 'Tool')
|
||||
});
|
||||
/** @type {JLINCConfig} */
|
||||
this.config = config;
|
||||
/** @type {Tool} */
|
||||
this.jlincAuthDecision = jlincAuthDecision;
|
||||
/** @type {Tool} */
|
||||
this.targetAuthorized = targetAuthorized;
|
||||
/** @type {Tool|null} */
|
||||
this.targetNotAuthorized = targetNotAuthorized;
|
||||
/** @type {JLINCTracer} */
|
||||
this.tracer = new JLINCTracer(config);
|
||||
/** @type {string} */
|
||||
this.authType = 'Tool';
|
||||
}
|
||||
|
||||
/**
|
||||
* @template TInput
|
||||
* @template {undefined | ToolRunnableConfig} TConfig
|
||||
* @template TOutput
|
||||
*
|
||||
* @param {TInput} input
|
||||
* @param {TConfig} [config]
|
||||
* @returns {Promise<ToolReturnType<TInput, TConfig, TOutput>>}
|
||||
*/
|
||||
async invoke(input, runManager) {
|
||||
return await authInvoke(this, input);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
JLINCAuthTool,
|
||||
};
|
||||
37
dist/auth/common.d.ts
vendored
Normal file
37
dist/auth/common.d.ts
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
export type JLINCConfig = {
|
||||
dataStoreApiUrl?: string | undefined;
|
||||
dataStoreApiKey?: string | undefined;
|
||||
archiveApiUrl?: string | undefined;
|
||||
archiveApiKey?: string | undefined;
|
||||
systemPrefix?: string | undefined;
|
||||
agreementId?: string | null | undefined;
|
||||
debug?: boolean | undefined;
|
||||
};
|
||||
/**
|
||||
* @param {any} settings
|
||||
* @param {any} input
|
||||
* @returns {Promise<any>} - The result of invoking the selected tool.
|
||||
*/
|
||||
export function authInvoke(settings: any, input: any): Promise<any>;
|
||||
/**
|
||||
* @param {any} - Authorized
|
||||
* @param {any|null} - NotAuthorized
|
||||
* @returns {string} - The description
|
||||
*/
|
||||
export function getDescription(authorized: any, unauthorized: any, type: any): string;
|
||||
/**
|
||||
* @typedef {Object} JLINCConfig
|
||||
* @property {string} [dataStoreApiUrl]
|
||||
* @property {string} [dataStoreApiKey]
|
||||
* @property {string} [archiveApiUrl]
|
||||
* @property {string} [archiveApiKey]
|
||||
* @property {string} [systemPrefix]
|
||||
* @property {string|null} [agreementId]
|
||||
* @property {boolean} [debug]
|
||||
*/
|
||||
/**
|
||||
* @param {any} - Authorized
|
||||
* @param {any|null} - NotAuthorized
|
||||
* @returns {string} - The description
|
||||
*/
|
||||
export function getName(authorized: any, unauthorized: any): string;
|
||||
85
dist/auth/common.js
vendored
Normal file
85
dist/auth/common.js
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
const axios = require("axios");
|
||||
|
||||
/**
|
||||
* @typedef {Object} JLINCConfig
|
||||
* @property {string} [dataStoreApiUrl]
|
||||
* @property {string} [dataStoreApiKey]
|
||||
* @property {string} [archiveApiUrl]
|
||||
* @property {string} [archiveApiKey]
|
||||
* @property {string} [systemPrefix]
|
||||
* @property {string|null} [agreementId]
|
||||
* @property {boolean} [debug]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {any} - Authorized
|
||||
* @param {any|null} - NotAuthorized
|
||||
* @returns {string} - The description
|
||||
*/
|
||||
function getName(authorized, unauthorized) {
|
||||
return unauthorized
|
||||
? `${authorized.name}-or-${unauthorized.name}`
|
||||
: `authorized-${authorized.name}`
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} - Authorized
|
||||
* @param {any|null} - NotAuthorized
|
||||
* @returns {string} - The description
|
||||
*/
|
||||
function getDescription(authorized, unauthorized, type) {
|
||||
return unauthorized ? `
|
||||
This is an authorization router ${type}. It decides whether
|
||||
to route input to an "authorized" or "not authorized" ${type}.
|
||||
|
||||
Authorized ${type}: ${authorized.name}
|
||||
Description: ${authorized.description}
|
||||
|
||||
Not Authorized ${type}: ${unauthorized.name}
|
||||
Description: ${unauthorized.description}
|
||||
|
||||
The LLM can assume both ${type}s are available, but JLINC Auth will
|
||||
only allow the Authorized ${type} to be called if you are authorized.
|
||||
` : `
|
||||
This is an authorization router ${type}. It decides whether
|
||||
to route input to an "authorized" ${type}.
|
||||
|
||||
Authorized ${type}: ${authorized.name}
|
||||
Description: ${authorized.description}
|
||||
|
||||
The LLM can assume the ${type} is available, but JLINC Auth will
|
||||
only allow the Authorized ${type} to be called if you are authorized.
|
||||
`
|
||||
}
|
||||
|
||||
function getLogName(target) {
|
||||
if (target.name) return target.name;
|
||||
if (target.lc_kwargs?.bound?.model) return target.lc_kwargs.bound.model;
|
||||
return 'no-name';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} settings
|
||||
* @param {any} input
|
||||
* @returns {Promise<any>} - The result of invoking the selected tool.
|
||||
*/
|
||||
async function authInvoke(settings, input) {
|
||||
const authorized = await settings.jlincAuthDecision.getAuth();
|
||||
let selectedTarget = settings.targetNotAuthorized
|
||||
if (authorized) {
|
||||
selectedTarget = settings.targetAuthorized
|
||||
}
|
||||
if (!selectedTarget)
|
||||
throw new Error("No valid resource available");
|
||||
if (settings.config.debug)
|
||||
console.log(`[JLINCAuth] Invoking ${getLogName(selectedTarget)} (${settings.authType}|${authorized})`);
|
||||
return await selectedTarget.invoke(input, {
|
||||
callbacks: [settings.tracer],
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
authInvoke,
|
||||
getDescription,
|
||||
getName,
|
||||
};
|
||||
4
dist/auth/index.d.ts
vendored
Normal file
4
dist/auth/index.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
import { JLINCAuthDecision } from "./JLINCAuthDecision.js";
|
||||
import { JLINCAuthBaseChatModel } from "./JLINCAuthBaseChatModel.js";
|
||||
import { JLINCAuthTool } from "./JLINCAuthTool.js";
|
||||
export { JLINCAuthDecision, JLINCAuthBaseChatModel, JLINCAuthTool };
|
||||
9
dist/auth/index.js
vendored
Normal file
9
dist/auth/index.js
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
const { JLINCAuthDecision } = require("./JLINCAuthDecision.js");
|
||||
const { JLINCAuthBaseChatModel } = require("./JLINCAuthBaseChatModel.js");
|
||||
const { JLINCAuthTool } = require("./JLINCAuthTool.js");
|
||||
|
||||
module.exports = {
|
||||
JLINCAuthDecision,
|
||||
JLINCAuthBaseChatModel,
|
||||
JLINCAuthTool,
|
||||
};
|
||||
5
dist/index.d.ts
vendored
Normal file
5
dist/index.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import { JLINCAuthDecision } from "./auth/index.js";
|
||||
import { JLINCAuthBaseChatModel } from "./auth/index.js";
|
||||
import { JLINCAuthTool } from "./auth/index.js";
|
||||
import { JLINCTracer } from "./tracer/index.js";
|
||||
export { JLINCAuthDecision, JLINCAuthBaseChatModel, JLINCAuthTool, JLINCTracer };
|
||||
13
dist/index.js
vendored
Normal file
13
dist/index.js
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
const {
|
||||
JLINCAuthDecision,
|
||||
JLINCAuthBaseChatModel,
|
||||
JLINCAuthTool,
|
||||
} = require("./auth/index.js");
|
||||
const { JLINCTracer } = require("./tracer/index.js");
|
||||
|
||||
module.exports = {
|
||||
JLINCAuthDecision,
|
||||
JLINCAuthBaseChatModel,
|
||||
JLINCAuthTool,
|
||||
JLINCTracer,
|
||||
};
|
||||
0
dist/tracer.js → dist/tracer/index.js
vendored
0
dist/tracer.js → dist/tracer/index.js
vendored
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@jlinc/langchain",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "jlinc-tracer",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.6.0",
|
||||
|
||||
10
package.json
10
package.json
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@jlinc/langchain",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.4",
|
||||
"description": "A LangChain integration that logs events to the JLINC API.",
|
||||
"main": "dist/tracer.js",
|
||||
"types": "dist/tracer.d.ts",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"test": "node test/test_tracer.js",
|
||||
"build": "tsc && cp src/tracer.js dist/"
|
||||
"test": "node test/test.js",
|
||||
"build": "rm -rf dist && cp -a src dist && tsc"
|
||||
},
|
||||
"homepage": "https://www.jlinc.com/",
|
||||
"repository": {
|
||||
|
||||
96
src/auth/JLINCAuthBaseChatModel.js
Normal file
96
src/auth/JLINCAuthBaseChatModel.js
Normal file
@@ -0,0 +1,96 @@
|
||||
const { BaseChatModel } = require("@langchain/core/language_models/chat_models");
|
||||
const { JLINCTracer } = require("../tracer/index.js");
|
||||
const { authDecide, authInvoke, getDescription, getName } = require("./common.js");
|
||||
|
||||
/**
|
||||
* @typedef {import('./common').JLINCConfig} JLINCConfig
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} JLINCAuthBaseChatModelFields
|
||||
* @property {JLINCConfig} config
|
||||
* @property {Tool} jlincAuthDecision
|
||||
* @property {BaseChatModel} targetAuthorized
|
||||
* @property {BaseChatModel|null} targetNotAuthorized
|
||||
*/
|
||||
|
||||
class JLINCAuthBaseChatModel extends BaseChatModel {
|
||||
/**
|
||||
* @param {JLINCAuthBaseChatModelFields} fields
|
||||
*/
|
||||
constructor({ config, jlincAuthDecision, targetAuthorized, targetNotAuthorized }) {
|
||||
super({
|
||||
name: "jlinc_auth_base_chat_model",
|
||||
description: "", // overridden dynamically
|
||||
});
|
||||
Object.defineProperty(this, "name", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: getName(targetAuthorized, targetNotAuthorized)
|
||||
});
|
||||
Object.defineProperty(this, "description", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: getDescription(targetAuthorized, targetNotAuthorized, 'BaseChatModel')
|
||||
});
|
||||
/** @type {JLINCConfig} */
|
||||
this.config = config;
|
||||
/** @type {Tool} */
|
||||
this.jlincAuthDecision = jlincAuthDecision;
|
||||
/** @type {BaseChatModel} */
|
||||
this.targetAuthorized = targetAuthorized;
|
||||
/** @type {BaseChatModel|null} */
|
||||
this.targetNotAuthorized = targetNotAuthorized;
|
||||
/** @type {JLINCTracer} */
|
||||
this.tracer = new JLINCTracer(config);
|
||||
/** @type {string} */
|
||||
this.authType = 'BaseChatModel';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BindToolsInput[]} tools - The tools to bind to the underlying models.
|
||||
* @param {Partial<CallOptions>} [kwargs] - Optional call options.
|
||||
* @returns {Runnable<BaseLanguageModelInput, OutputMessageType, CallOptions>}
|
||||
* A new JLINCAuthBaseChatModel instance whose underlying models
|
||||
* have been bound with the provided tools.
|
||||
*/
|
||||
bindTools(tools, kwargs = {}) {
|
||||
if (this.config.debug)
|
||||
console.log(`[JLINCAuth] Binding tools to BaseChatModels`);
|
||||
const authorizedBound = this.targetAuthorized.bindTools(tools, kwargs);
|
||||
const notAuthorizedBound = this.targetNotAuthorized
|
||||
? this.targetNotAuthorized.bindTools(tools, kwargs)
|
||||
: null;
|
||||
return new JLINCAuthBaseChatModel({
|
||||
config: this.config,
|
||||
jlincAuthDecision: this.jlincAuthDecision,
|
||||
targetAuthorized: authorizedBound,
|
||||
targetNotAuthorized: notAuthorizedBound,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} - type
|
||||
*/
|
||||
_llmType() {
|
||||
const type = this.targetAuthorized?._llmType?.() ?? "unknown";
|
||||
if (this.config?.debug)
|
||||
console.log(`[JLINCAuth] Returning LLM type: ${type}`);
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BaseLanguageModelInput} input
|
||||
* @param {Partial<CallOptions>} [options] - Optional call options.
|
||||
* @returns {Promise<OutputMessageType>} A promise that resolves with the output.
|
||||
*/
|
||||
async invoke(input, runManager) {
|
||||
return await authInvoke(this, input);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
JLINCAuthBaseChatModel,
|
||||
};
|
||||
79
src/auth/JLINCAuthDecision.js
Normal file
79
src/auth/JLINCAuthDecision.js
Normal file
@@ -0,0 +1,79 @@
|
||||
const { Tool } = require("@langchain/core/tools");
|
||||
const axios = require("axios");
|
||||
|
||||
/**
|
||||
* @typedef {import('./common').JLINCConfig} JLINCConfig
|
||||
*/
|
||||
|
||||
class JLINCAuthDecision extends Tool {
|
||||
/**
|
||||
* @param {JLINCConfig} fields
|
||||
*/
|
||||
constructor(config) {
|
||||
super({
|
||||
name: "jlinc_auth",
|
||||
description: "Authorization via AuthZEN format.",
|
||||
});
|
||||
Object.defineProperty(this, "name", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: "jlinc-auth"
|
||||
});
|
||||
Object.defineProperty(this, "description", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: "Authorization via AuthZEN format."
|
||||
});
|
||||
/** @type {JLINCConfig} */
|
||||
this.config = config;
|
||||
/** @type {Boolean} */
|
||||
this.authenticated = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} payload
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
async postToApi(auth) {
|
||||
const response = await axios.post(
|
||||
`${this.config.dataStoreApiUrl}/api/v1/auth`,
|
||||
auth,
|
||||
{
|
||||
headers: {
|
||||
'Authorization': `Bearer ${this.config.dataStoreApiKey}`,
|
||||
}
|
||||
}
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {auth} auth
|
||||
* @returns {Promise<boolean>} - authenticated
|
||||
*/
|
||||
async evaluate(auth) {
|
||||
this.authenticated = false;
|
||||
try {
|
||||
this.authenticated = (await this.postToApi(auth)).decision;
|
||||
} catch (e) {
|
||||
console.log(`[JLINCAuthTool] Error connecting to API, flagging as unauthorized`);
|
||||
if (this.config.debug) console.error(e)
|
||||
}
|
||||
if (this.config.debug)
|
||||
console.log(`[JLINCAuth] Got authorization of: ${this.authenticated}`);
|
||||
return this.authenticated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean} - authenticated
|
||||
*/
|
||||
getAuth() {
|
||||
return this.authenticated;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
JLINCAuthDecision,
|
||||
};
|
||||
68
src/auth/JLINCAuthTool.js
Normal file
68
src/auth/JLINCAuthTool.js
Normal file
@@ -0,0 +1,68 @@
|
||||
const { Tool } = require("@langchain/core/tools");
|
||||
const { JLINCTracer } = require("../tracer/index.js");
|
||||
const { authDecide, authInvoke, getDescription, getName } = require("./common.js");
|
||||
|
||||
/**
|
||||
* @typedef {import('./common').JLINCConfig} JLINCConfig
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} JLINCAuthToolFields
|
||||
* @property {JLINCConfig} config
|
||||
* @property {Tool} jlincAuthDecision
|
||||
* @property {Tool} targetAuthorized
|
||||
* @property {Tool|null} targetNotAuthorized
|
||||
*/
|
||||
|
||||
class JLINCAuthTool extends Tool {
|
||||
/**
|
||||
* @param {JLINCAuthToolFields} fields
|
||||
*/
|
||||
constructor({ config, jlincAuthDecision, targetAuthorized, targetNotAuthorized }) {
|
||||
super({
|
||||
name: "jlinc_auth_tool",
|
||||
description: "", // overridden dynamically
|
||||
});
|
||||
Object.defineProperty(this, "name", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: getName(targetAuthorized, targetNotAuthorized)
|
||||
});
|
||||
Object.defineProperty(this, "description", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: getDescription(targetAuthorized, targetNotAuthorized, 'Tool')
|
||||
});
|
||||
/** @type {JLINCConfig} */
|
||||
this.config = config;
|
||||
/** @type {Tool} */
|
||||
this.jlincAuthDecision = jlincAuthDecision;
|
||||
/** @type {Tool} */
|
||||
this.targetAuthorized = targetAuthorized;
|
||||
/** @type {Tool|null} */
|
||||
this.targetNotAuthorized = targetNotAuthorized;
|
||||
/** @type {JLINCTracer} */
|
||||
this.tracer = new JLINCTracer(config);
|
||||
/** @type {string} */
|
||||
this.authType = 'Tool';
|
||||
}
|
||||
|
||||
/**
|
||||
* @template TInput
|
||||
* @template {undefined | ToolRunnableConfig} TConfig
|
||||
* @template TOutput
|
||||
*
|
||||
* @param {TInput} input
|
||||
* @param {TConfig} [config]
|
||||
* @returns {Promise<ToolReturnType<TInput, TConfig, TOutput>>}
|
||||
*/
|
||||
async invoke(input, runManager) {
|
||||
return await authInvoke(this, input);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
JLINCAuthTool,
|
||||
};
|
||||
85
src/auth/common.js
Normal file
85
src/auth/common.js
Normal file
@@ -0,0 +1,85 @@
|
||||
const axios = require("axios");
|
||||
|
||||
/**
|
||||
* @typedef {Object} JLINCConfig
|
||||
* @property {string} [dataStoreApiUrl]
|
||||
* @property {string} [dataStoreApiKey]
|
||||
* @property {string} [archiveApiUrl]
|
||||
* @property {string} [archiveApiKey]
|
||||
* @property {string} [systemPrefix]
|
||||
* @property {string|null} [agreementId]
|
||||
* @property {boolean} [debug]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {any} - Authorized
|
||||
* @param {any|null} - NotAuthorized
|
||||
* @returns {string} - The description
|
||||
*/
|
||||
function getName(authorized, unauthorized) {
|
||||
return unauthorized
|
||||
? `${authorized.name}-or-${unauthorized.name}`
|
||||
: `authorized-${authorized.name}`
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} - Authorized
|
||||
* @param {any|null} - NotAuthorized
|
||||
* @returns {string} - The description
|
||||
*/
|
||||
function getDescription(authorized, unauthorized, type) {
|
||||
return unauthorized ? `
|
||||
This is an authorization router ${type}. It decides whether
|
||||
to route input to an "authorized" or "not authorized" ${type}.
|
||||
|
||||
Authorized ${type}: ${authorized.name}
|
||||
Description: ${authorized.description}
|
||||
|
||||
Not Authorized ${type}: ${unauthorized.name}
|
||||
Description: ${unauthorized.description}
|
||||
|
||||
The LLM can assume both ${type}s are available, but JLINC Auth will
|
||||
only allow the Authorized ${type} to be called if you are authorized.
|
||||
` : `
|
||||
This is an authorization router ${type}. It decides whether
|
||||
to route input to an "authorized" ${type}.
|
||||
|
||||
Authorized ${type}: ${authorized.name}
|
||||
Description: ${authorized.description}
|
||||
|
||||
The LLM can assume the ${type} is available, but JLINC Auth will
|
||||
only allow the Authorized ${type} to be called if you are authorized.
|
||||
`
|
||||
}
|
||||
|
||||
function getLogName(target) {
|
||||
if (target.name) return target.name;
|
||||
if (target.lc_kwargs?.bound?.model) return target.lc_kwargs.bound.model;
|
||||
return 'no-name';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} settings
|
||||
* @param {any} input
|
||||
* @returns {Promise<any>} - The result of invoking the selected tool.
|
||||
*/
|
||||
async function authInvoke(settings, input) {
|
||||
const authorized = await settings.jlincAuthDecision.getAuth();
|
||||
let selectedTarget = settings.targetNotAuthorized
|
||||
if (authorized) {
|
||||
selectedTarget = settings.targetAuthorized
|
||||
}
|
||||
if (!selectedTarget)
|
||||
throw new Error("No valid resource available");
|
||||
if (settings.config.debug)
|
||||
console.log(`[JLINCAuth] Invoking ${getLogName(selectedTarget)} (${settings.authType}|${authorized})`);
|
||||
return await selectedTarget.invoke(input, {
|
||||
callbacks: [settings.tracer],
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
authInvoke,
|
||||
getDescription,
|
||||
getName,
|
||||
};
|
||||
9
src/auth/index.js
Normal file
9
src/auth/index.js
Normal file
@@ -0,0 +1,9 @@
|
||||
const { JLINCAuthDecision } = require("./JLINCAuthDecision.js");
|
||||
const { JLINCAuthBaseChatModel } = require("./JLINCAuthBaseChatModel.js");
|
||||
const { JLINCAuthTool } = require("./JLINCAuthTool.js");
|
||||
|
||||
module.exports = {
|
||||
JLINCAuthDecision,
|
||||
JLINCAuthBaseChatModel,
|
||||
JLINCAuthTool,
|
||||
};
|
||||
13
src/index.js
Normal file
13
src/index.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const {
|
||||
JLINCAuthDecision,
|
||||
JLINCAuthBaseChatModel,
|
||||
JLINCAuthTool,
|
||||
} = require("./auth/index.js");
|
||||
const { JLINCTracer } = require("./tracer/index.js");
|
||||
|
||||
module.exports = {
|
||||
JLINCAuthDecision,
|
||||
JLINCAuthBaseChatModel,
|
||||
JLINCAuthTool,
|
||||
JLINCTracer,
|
||||
};
|
||||
116
test/test.js
Normal file
116
test/test.js
Normal file
@@ -0,0 +1,116 @@
|
||||
const { ChatOpenAI } = require("@langchain/openai");
|
||||
const { awaitAllCallbacks } = require("@langchain/core/callbacks/promises");
|
||||
const { Calculator } = require("@langchain/community/tools/calculator");
|
||||
const { AgentExecutor, createToolCallingAgent } = require("langchain/agents");
|
||||
const { ChatPromptTemplate } = require("@langchain/core/prompts");
|
||||
const { JLINCTracer, JLINCAuthDecision, JLINCAuthBaseChatModel, JLINCAuthTool } = require("../src/index.js");
|
||||
|
||||
class CalculatorPrivate extends Calculator {
|
||||
static lc_name() {
|
||||
return "CalculatorPrivate";
|
||||
}
|
||||
}
|
||||
|
||||
class CalculatorPublic extends Calculator {
|
||||
static lc_name() {
|
||||
return "CalculatorPublic";
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const config = {
|
||||
dataStoreApiUrl: "http://localhost:9090",
|
||||
dataStoreApiKey: process.env.JLINC_DATA_STORE_API_KEY,
|
||||
archiveApiUrl: "http://localhost:9090",
|
||||
archiveApiKey: process.env.JLINC_ARCHIVE_API_KEY,
|
||||
agreementId: "00000000-0000-0000-0000-000000000000",
|
||||
systemPrefix: "TestTracerJlinc",
|
||||
debug: true,
|
||||
}
|
||||
|
||||
const jlincAuthDecision = new JLINCAuthDecision(config);
|
||||
const auth = {
|
||||
subject: {
|
||||
type: "user",
|
||||
id: "tester",
|
||||
},
|
||||
action: {
|
||||
name: "read",
|
||||
},
|
||||
resource: {
|
||||
type: "data",
|
||||
id: "1234",
|
||||
properties: {
|
||||
ownerID: "tester@test.com",
|
||||
}
|
||||
}
|
||||
}
|
||||
await jlincAuthDecision.evaluate(auth);
|
||||
|
||||
const tracer = new JLINCTracer(config);
|
||||
|
||||
const authorizedLlm = new ChatOpenAI({
|
||||
openAIApiKey: "n/a",
|
||||
configuration: {
|
||||
baseURL: "http://localhost:1234/v1",
|
||||
},
|
||||
modelName: "meta-llama-3.1-8b-instruct",
|
||||
});
|
||||
const notAuthorizedLlm = new ChatOpenAI({
|
||||
openAIApiKey: "n/a",
|
||||
configuration: {
|
||||
baseURL: "http://localhost:1234/v1",
|
||||
},
|
||||
modelName: "hermes-3-llama-3.1-8b",
|
||||
});
|
||||
const llm = new JLINCAuthBaseChatModel({
|
||||
config,
|
||||
jlincAuthDecision,
|
||||
targetAuthorized: authorizedLlm,
|
||||
targetNotAuthorized: notAuthorizedLlm, // Optional
|
||||
});
|
||||
|
||||
const calculatorPublic = new CalculatorPublic();
|
||||
calculatorPublic.name = 'calculator_public';
|
||||
const calculatorPrivate = new CalculatorPrivate();
|
||||
calculatorPrivate.name = 'calculator_private';
|
||||
const jlincAuthTool = new JLINCAuthTool({
|
||||
config,
|
||||
jlincAuthDecision,
|
||||
targetAuthorized: calculatorPublic,
|
||||
targetNotAuthorized: calculatorPrivate, // Optional
|
||||
});
|
||||
const tools = [jlincAuthTool];
|
||||
|
||||
const prompt = ChatPromptTemplate.fromMessages([
|
||||
["system", "You are a helpful assistant"],
|
||||
["placeholder", "{chat_history}"],
|
||||
["human", "{input}"],
|
||||
["placeholder", "{agent_scratchpad}"],
|
||||
]);
|
||||
|
||||
const agent = createToolCallingAgent({ llm, tools, prompt });
|
||||
|
||||
const agentExecutor = new AgentExecutor({
|
||||
agent,
|
||||
tools,
|
||||
});
|
||||
|
||||
try {
|
||||
const r = await agentExecutor.invoke({ input: "Add 1 + 1. If a function call is used, tell me the output of the function call." }, { callbacks: [tracer] });
|
||||
|
||||
// The next invocation requires a reauth for any future calls to the agent:
|
||||
auth.action.name = "write";
|
||||
jlincAuthDecision.evaluate(auth);
|
||||
|
||||
console.log(`\nResult`)
|
||||
console.log(`---------------------------------------------`)
|
||||
console.log(r)
|
||||
} catch (err) {
|
||||
console.error("Error calling LLM:", err);
|
||||
} finally {
|
||||
await awaitAllCallbacks();
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
@@ -1,56 +0,0 @@
|
||||
const { ChatOpenAI } = require("@langchain/openai");
|
||||
const { awaitAllCallbacks } = require("@langchain/core/callbacks/promises");
|
||||
const { Calculator } = require("@langchain/community/tools/calculator");
|
||||
const { AgentExecutor, createToolCallingAgent } = require("langchain/agents");
|
||||
const { ChatPromptTemplate } = require("@langchain/core/prompts");
|
||||
const { JLINCTracer } = require("../src/tracer.js");
|
||||
|
||||
async function main() {
|
||||
const tracer = new JLINCTracer({
|
||||
dataStoreApiUrl: "http://localhost:9090",
|
||||
dataStoreApiKey: process.env.JLINC_DATA_STORE_API_KEY,
|
||||
archiveApiUrl: "http://localhost:9090",
|
||||
archiveApiKey: process.env.JLINC_ARCHIVE_API_KEY,
|
||||
agreementId: "00000000-0000-0000-0000-000000000000",
|
||||
systemPrefix: "TracerTest",
|
||||
debug: true,
|
||||
});
|
||||
|
||||
const llm = new ChatOpenAI({
|
||||
openAIApiKey: "n/a",
|
||||
configuration: {
|
||||
baseURL: "http://localhost:1234/v1",
|
||||
},
|
||||
modelName: "meta-llama-3.1-8b-instruct",
|
||||
});
|
||||
|
||||
const calculator = new Calculator();
|
||||
const tools = [calculator];
|
||||
|
||||
const prompt = ChatPromptTemplate.fromMessages([
|
||||
["system", "You are a helpful assistant"],
|
||||
["placeholder", "{chat_history}"],
|
||||
["human", "{input}"],
|
||||
["placeholder", "{agent_scratchpad}"],
|
||||
]);
|
||||
|
||||
const agent = createToolCallingAgent({ llm, tools, prompt });
|
||||
|
||||
const agentExecutor = new AgentExecutor({
|
||||
agent,
|
||||
tools,
|
||||
});
|
||||
|
||||
try {
|
||||
const r = await agentExecutor.invoke({ input: "Add 1 + 1" }, {callbacks: [tracer]});
|
||||
console.log(`\nResult`)
|
||||
console.log(`---------------------------------------------`)
|
||||
console.log(r)
|
||||
} catch (err) {
|
||||
console.error("Error calling LLM:", err);
|
||||
} finally {
|
||||
await awaitAllCallbacks();
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
Reference in New Issue
Block a user