Skip to content

Commit

Permalink
Scaffold otel + arize phoenix integration
Browse files Browse the repository at this point in the history
  • Loading branch information
cephalization committed Oct 3, 2024
1 parent eaa70f0 commit 555f42d
Show file tree
Hide file tree
Showing 19 changed files with 1,942 additions and 114 deletions.
7 changes: 7 additions & 0 deletions packages/cannoli-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,18 @@
"typescript": "^5.3.3"
},
"dependencies": {
"@arizeai/openinference-instrumentation-langchain": "0.2.0",
"@arizeai/openinference-semantic-conventions": "0.10.0",
"@langchain/anthropic": "0.2.1",
"@langchain/community": "0.2.12",
"@langchain/core": "0.2.7",
"@langchain/google-genai": "0.0.19",
"@langchain/groq": "0.0.12",
"@langchain/openai": "0.1.3",
"@opentelemetry/exporter-trace-otlp-proto": "0.53.0",
"@opentelemetry/instrumentation": "0.53.0",
"@opentelemetry/resources": "1.26.0",
"@opentelemetry/sdk-trace-web": "1.26.0",
"js-yaml": "^4.1.0",
"langchain": "0.2.5",
"nanoid": "5.0.7",
Expand All @@ -51,6 +57,7 @@
"tiny-invariant": "^1.3.1",
"tslib": "2.4.0",
"tsup": "^8.0.2",
"web-instrumentation-langchain": "workspace:*",
"zod": "3.23.8"
}
}
54 changes: 54 additions & 0 deletions packages/cannoli-core/src/instrumentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { ConsoleSpanExporter, WebTracerProvider, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-web"
import { SEMRESATTRS_PROJECT_NAME } from "@arizeai/openinference-semantic-conventions";
import { Resource } from "@opentelemetry/resources"
import * as lcCallbackManager from "@langchain/core/callbacks/manager";
import { LangChainInstrumentation } from "web-instrumentation-langchain";

import { TracingConfig } from "src/run"
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";

const instrumentPhoenixLangchain = () => {
const lcInstrumentation = new LangChainInstrumentation();
lcInstrumentation.manuallyInstrument(lcCallbackManager);

console.log("🔎 Phoenix Langchain instrumentation enabled 🔎")
}

export const createPhoenixWebTracerProvider = ({ tracingConfig }: { tracingConfig: TracingConfig }) => {
if (!tracingConfig.phoenix?.enabled) {
return
}

try {

const provider = new WebTracerProvider({
resource: new Resource({
[SEMRESATTRS_PROJECT_NAME]: tracingConfig.phoenix.projectName,

}),
})

provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()))
provider.addSpanProcessor(new SimpleSpanProcessor(new OTLPTraceExporter({
url: tracingConfig.phoenix.baseUrl,
headers: {
// allow cross-origin requests
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization, Content-Length, X-Requested-With, Accept, Origin",
"Access-Control-Allow-Credentials": "true",
}
})))

provider.register()

console.log("🔎 Phoenix tracing enabled 🔎")

instrumentPhoenixLangchain()

return provider
} catch (error) {
console.error("Error enabling Phoenix tracing", error)
}
}

Empty file.
4 changes: 4 additions & 0 deletions packages/cannoli-core/src/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export type SupportedProviders = "openai" | "ollama" | "gemini" | "anthropic" |

import { z } from "zod";
import invariant from "tiny-invariant";
import { TracingConfig } from "src/run";

export const GenericFunctionCallSchema = z.object({
name: z.string(),
Expand Down Expand Up @@ -69,6 +70,7 @@ export type LLMConfig = (Omit<GenericModelConfig, "provider"> & { provider: Supp

type ConstructorArgs = {
configs: LLMConfig[];
tracingConfig?: TracingConfig | null;
valtownApiKey?: string;
};

Expand Down Expand Up @@ -105,6 +107,7 @@ export class LLMProvider {
getDefaultConfigByProvider?: GetDefaultsByProvider;
initialized = false;
valtownApiKey?: string;
tracingConfig?: TracingConfig | null;

constructor(initArgs: ConstructorArgs) {
this.init(initArgs);
Expand All @@ -115,6 +118,7 @@ export class LLMProvider {
this.provider = initArgs.configs[0].provider as SupportedProviders;
this.baseConfig = initArgs.configs[0];
this.valtownApiKey = initArgs.valtownApiKey;
this.tracingConfig = initArgs.tracingConfig;
this.getDefaultConfigByProvider = (provider: SupportedProviders) => {
return initArgs.configs.find((config) => config.provider === provider);
}
Expand Down
28 changes: 26 additions & 2 deletions packages/cannoli-core/src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { CannoliGroup } from "./graph/objects/vertices/CannoliGroup";
import { CannoliNode } from "./graph/objects/vertices/CannoliNode";
import { parseNamedNode } from "./utility";
import { resultsRun } from "./cannoli";
import { z } from "zod";
import { createPhoenixWebTracerProvider } from "src/instrumentation";

export interface HttpTemplate {
id: string;
Expand Down Expand Up @@ -100,6 +102,17 @@ export interface ModelUsage {

export type ChatRole = "user" | "assistant" | "system";

const tracingConfigSchema = z.object({
phoenix: z.object({
enabled: z.boolean().default(false),
apiKey: z.string().optional(),
baseUrl: z.string(),
projectName: z.string().default("cannoli"),
}).nullish(),
});

export type TracingConfig = z.infer<typeof tracingConfigSchema>;

enum DagCheckState {
UNVISITED,
VISITING,
Expand All @@ -117,7 +130,7 @@ export interface RunArgs {
cannoli: unknown;
llmConfigs?: LLMConfig[];
fetcher?: ResponseTextFetcher;
config?: Record<string, string | number | boolean>;
config?: Record<string, unknown>;
secrets?: Record<string, string>;
args?: Record<string, string>;
onFinish?: (stoppage: Stoppage) => void;
Expand Down Expand Up @@ -150,6 +163,7 @@ export class Run {
stopTime: number | null = null;
currentNote: string | null = null;
selection: string | null = null;
tracingConfig: TracingConfig | null = null;

subcannoliCallback: (cannoli: unknown, inputVariables: Record<string, string>, scIsMock: boolean) => Promise<Record<string, string>>;

Expand Down Expand Up @@ -192,6 +206,16 @@ export class Run {

this.args = args ?? null;

const tracingConfig = tracingConfigSchema.safeParse(config?.tracingConfig);

if (tracingConfig.success) {
this.tracingConfig = tracingConfig.data;
}

if (this.tracingConfig) {
createPhoenixWebTracerProvider({ tracingConfig: this.tracingConfig })
}

let parsedCannoliJSON: CanvasData;

try {
Expand Down Expand Up @@ -1132,4 +1156,4 @@ export class Run {
}
}
}
}
}
1 change: 1 addition & 0 deletions packages/cannoli-plugin/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,7 @@ export default class Cannoli extends Plugin {
...(this.settings.contentIsColorless ? { contentIsColorless: this.settings.contentIsColorless } : {}),
...(!chatFormatStringIsDefault ? { chatFormatString: this.settings.chatFormatString } : {}),
...(this.settings.enableVision !== undefined ? { enableVision: this.settings.enableVision } : {}),
tracingConfig: this.settings.tracingConfig,
};
}

Expand Down
84 changes: 84 additions & 0 deletions packages/cannoli-plugin/src/settings/sections/tracingSettings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { Setting } from "obsidian";
import Cannoli from "src/main";
import { TracingConfig } from "@deablabs/cannoli-core";
import { DEFAULT_SETTINGS } from "src/settings/settings";

const defaultPhoenixTracingConfig: NonNullable<TracingConfig["phoenix"]> = DEFAULT_SETTINGS.tracingConfig.phoenix!

export function createTracingSettings(containerEl: HTMLElement, plugin: Cannoli, display: () => void): void {
// heading
containerEl.createEl("h1", { text: "Tracing" });

new Setting(containerEl)
.setName("Phoenix Tracing")
.setDesc("Enable Phoenix tracing for your Cannoli runs. Phoenix is a data tracing system that allows you to observe the history of your runs, and optimize your prompts over time.")
.addToggle((toggle) => {
toggle.setValue(plugin.settings.tracingConfig.phoenix?.enabled ?? false);
toggle.onChange(async (value) => {
if (plugin.settings.tracingConfig.phoenix) {
plugin.settings.tracingConfig.phoenix.enabled = value;
} else {
plugin.settings.tracingConfig.phoenix = {
...defaultPhoenixTracingConfig,
enabled: value,
}
}
await plugin.saveSettings();
display();
});
});

new Setting(containerEl)
.setName("Phoenix Project Name")
.setDesc("The name of the project to use for your Phoenix tracing. This is used to identify the project in the Phoenix console.")
.addText((text) => {
text.setValue(plugin.settings.tracingConfig.phoenix?.projectName ?? defaultPhoenixTracingConfig.projectName);
text.onChange(async (value) => {
if (plugin.settings.tracingConfig.phoenix) {
plugin.settings.tracingConfig.phoenix.projectName = value;
} else {
plugin.settings.tracingConfig.phoenix = {
...defaultPhoenixTracingConfig,
projectName: value,
}
}
await plugin.saveSettings();
});
});

new Setting(containerEl)
.setName("Phoenix Base URL")
.setDesc("The base URL for your Phoenix tracing. This is used to send your tracing data to the Phoenix server.")
.addText((text) => {
text.setValue(plugin.settings.tracingConfig.phoenix?.baseUrl ?? defaultPhoenixTracingConfig.baseUrl);
text.onChange(async (value) => {
if (plugin.settings.tracingConfig.phoenix) {
plugin.settings.tracingConfig.phoenix.baseUrl = value;
} else {
plugin.settings.tracingConfig.phoenix = {
...defaultPhoenixTracingConfig,
baseUrl: value,
}
}
await plugin.saveSettings();
});
});

new Setting(containerEl)
.setName("Phoenix API Key")
.setDesc("The API key to use for your Phoenix tracing. This is used to authenticate your tracing data to the Phoenix server.")
.addText((text) => {
text.setValue(plugin.settings.tracingConfig.phoenix?.apiKey ?? defaultPhoenixTracingConfig.apiKey ?? "");
text.onChange(async (value) => {
if (plugin.settings.tracingConfig.phoenix) {
plugin.settings.tracingConfig.phoenix.apiKey = value;
} else {
plugin.settings.tracingConfig.phoenix = {
...defaultPhoenixTracingConfig,
apiKey: value,
}
}
await plugin.saveSettings();
});
});
}
Loading

0 comments on commit 555f42d

Please sign in to comment.