Skip to content

Commit 5ca15aa

Browse files
committed
[Components] langfuse - new components
1 parent ec3fd3b commit 5ca15aa

File tree

12 files changed

+605
-8
lines changed

12 files changed

+605
-8
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import app from "../../langfuse.app.mjs";
2+
3+
export default {
4+
key: "langfuse-add-feedback",
5+
name: "Add Feedback",
6+
description: "Attach user feedback to an existing trace in Langfuse. [See the documentation](https://api.reference.langfuse.com/#tag/comments/POST/api/public/comments).",
7+
version: "0.0.1",
8+
type: "action",
9+
props: {
10+
app,
11+
projectId: {
12+
propDefinition: [
13+
app,
14+
"projectId",
15+
],
16+
},
17+
objectType: {
18+
propDefinition: [
19+
app,
20+
"objectType",
21+
],
22+
},
23+
objectId: {
24+
type: "string",
25+
label: "Object ID",
26+
description: "The id of the object to attach the comment to. If this does not reference a valid existing object, an error will be thrown.",
27+
},
28+
content: {
29+
type: "string",
30+
label: "Content",
31+
description: "The content of the comment. May include markdown. Currently limited to 3000 characters.",
32+
},
33+
},
34+
methods: {
35+
addFeedback(args = {}) {
36+
return this.app.post({
37+
path: "/comments",
38+
...args,
39+
});
40+
},
41+
},
42+
async run({ $ }) {
43+
const {
44+
addFeedback,
45+
projectId,
46+
objectType,
47+
objectId,
48+
content,
49+
} = this;
50+
51+
const response = await addFeedback({
52+
$,
53+
data: {
54+
projectId,
55+
objectType,
56+
objectId,
57+
content,
58+
},
59+
});
60+
61+
$.export("$summary", "Successfully added feedback.");
62+
return response;
63+
},
64+
};
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { v4 as uuid } from "uuid";
2+
import app from "../../langfuse.app.mjs";
3+
import constants from "../../common/constants.mjs";
4+
5+
export default {
6+
key: "langfuse-log-trace",
7+
name: "Log Trace",
8+
description: "Log a new trace in LangFuse with details. [See the documentation](https://api.reference.langfuse.com/#tag/ingestion/POST/api/public/ingestion).",
9+
version: "0.0.1",
10+
type: "action",
11+
props: {
12+
app,
13+
name: {
14+
type: "string",
15+
label: "Name",
16+
description: "The name of the trace",
17+
},
18+
input: {
19+
type: "string",
20+
label: "Input",
21+
description: "The input of the trace",
22+
},
23+
output: {
24+
type: "string",
25+
label: "Output",
26+
description: "The output of the trace",
27+
},
28+
userId: {
29+
type: "string",
30+
label: "User ID",
31+
description: "The ID of the user",
32+
optional: true,
33+
},
34+
sessionId: {
35+
type: "string",
36+
label: "Session ID",
37+
description: "The ID of the session",
38+
optional: true,
39+
},
40+
release: {
41+
type: "string",
42+
label: "Release",
43+
description: "The release of the trace",
44+
optional: true,
45+
},
46+
version: {
47+
type: "string",
48+
label: "Version",
49+
description: "The version of the trace",
50+
optional: true,
51+
},
52+
metadata: {
53+
type: "string",
54+
label: "Metadata",
55+
description: "The metadata of the trace",
56+
optional: true,
57+
},
58+
tags: {
59+
type: "string[]",
60+
label: "Tags",
61+
description: "The tags of the trace",
62+
optional: true,
63+
},
64+
},
65+
methods: {
66+
batchIngestion(args = {}) {
67+
return this.app.post({
68+
path: "/ingestion",
69+
...args,
70+
});
71+
},
72+
},
73+
async run({ $ }) {
74+
const {
75+
batchIngestion,
76+
name,
77+
userId,
78+
input,
79+
output,
80+
sessionId,
81+
release,
82+
version,
83+
metadata,
84+
tags,
85+
} = this;
86+
87+
const timestamp = new Date().toISOString();
88+
const id = uuid();
89+
90+
const response = await batchIngestion({
91+
$,
92+
data: {
93+
batch: [
94+
{
95+
id,
96+
timestamp,
97+
type: constants.INGESTION_TYPE.TRACE_CREATE,
98+
body: {
99+
id,
100+
timestamp,
101+
name,
102+
userId,
103+
input,
104+
output,
105+
sessionId,
106+
release,
107+
version,
108+
metadata,
109+
tags,
110+
public: true,
111+
},
112+
},
113+
],
114+
},
115+
});
116+
$.export("$summary", "Successfully logged a new trace");
117+
return response;
118+
},
119+
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
const REGION_PLACEHOLDER = "{region}";
2+
const BASE_URL = "https://{region}.langfuse.com";
3+
const VERSION_PATH = "/api/public";
4+
5+
const INGESTION_TYPE = {
6+
TRACE_CREATE: "trace-create",
7+
SCORE_CREATE: "score-create",
8+
SPAN_CREATE: "span-create",
9+
SPAN_UPDATE: "span-update",
10+
GENERATION_CREATE: "generation-create",
11+
GENERATION_UPDATE: "generation-update",
12+
EVENT_CREATE: "event-create",
13+
SDK_LOG: "sdk-log",
14+
OBSERVATION_CREATE: "observation-create",
15+
OBSERVATION_UPDATE: "observation-update",
16+
};
17+
18+
const LAST_DATE_AT = "lastDateAt";
19+
const IS_FIRST_RUN = "isFirstRun";
20+
const DEFAULT_LIMIT = 100;
21+
const DEFAULT_MAX = 1000;
22+
23+
export default {
24+
REGION_PLACEHOLDER,
25+
BASE_URL,
26+
VERSION_PATH,
27+
INGESTION_TYPE,
28+
LAST_DATE_AT,
29+
IS_FIRST_RUN,
30+
DEFAULT_LIMIT,
31+
DEFAULT_MAX,
32+
};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
async function iterate(iterations) {
2+
const items = [];
3+
for await (const item of iterations) {
4+
items.push(item);
5+
}
6+
return items;
7+
}
8+
9+
function getNestedProperty(obj, propertyString) {
10+
const properties = propertyString.split(".");
11+
return properties.reduce((prev, curr) => prev?.[curr], obj);
12+
}
13+
14+
export default {
15+
iterate,
16+
getNestedProperty,
17+
};
Lines changed: 135 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,142 @@
1+
import { axios } from "@pipedream/platform";
2+
import utils from "./common/utils.mjs";
3+
import constants from "./common/constants.mjs";
4+
15
export default {
26
type: "app",
37
app: "langfuse",
4-
propDefinitions: {},
8+
propDefinitions: {
9+
projectId: {
10+
type: "string",
11+
label: "Trace ID",
12+
description: "The ID of the trace to attach feedback to or to filter by for events.",
13+
async options() {
14+
const { data } = await this.listProjects();
15+
return data.map(({
16+
id: value, name: label,
17+
}) => ({
18+
label,
19+
value,
20+
}));
21+
},
22+
},
23+
objectType: {
24+
type: "string",
25+
label: "Object Type",
26+
description: "The type of object to attach feedback to.",
27+
options: [
28+
"TRACE",
29+
"OBSERVATION",
30+
"SESSION",
31+
"PROMPT",
32+
],
33+
},
34+
},
535
methods: {
6-
// this.$auth contains connected account data
7-
authKeys() {
8-
console.log(Object.keys(this.$auth));
36+
getUrl(path) {
37+
const baseUrl = constants.BASE_URL
38+
.replace(constants.REGION_PLACEHOLDER, this.$auth.region);
39+
return `${baseUrl}${constants.VERSION_PATH}${path}`;
40+
},
41+
getAuth() {
42+
const {
43+
public_key: username,
44+
secret_key: password,
45+
} = this.$auth;
46+
return {
47+
username,
48+
password,
49+
};
50+
},
51+
_makeRequest({
52+
$ = this, path, ...args
53+
} = {}) {
54+
return axios($, {
55+
...args,
56+
debug: true,
57+
url: this.getUrl(path),
58+
auth: this.getAuth(),
59+
headers: {
60+
"Content-Type": "application/json",
61+
},
62+
});
63+
},
64+
post(args = {}) {
65+
return this._makeRequest({
66+
method: "POST",
67+
...args,
68+
});
69+
},
70+
listProjects(args = {}) {
71+
return this._makeRequest({
72+
path: "/projects",
73+
...args,
74+
});
75+
},
76+
listTraces(args = {}) {
77+
return this._makeRequest({
78+
path: "/traces",
79+
...args,
80+
});
81+
},
82+
listScores(args = {}) {
83+
return this._makeRequest({
84+
path: "/scores",
85+
...args,
86+
});
87+
},
88+
async *getIterations({
89+
resourcesFn, resourcesFnArgs, resourceName,
90+
lastDateAt, dateField,
91+
max = constants.DEFAULT_MAX,
92+
}) {
93+
let page = 1;
94+
let resourcesCount = 0;
95+
96+
while (true) {
97+
const response =
98+
await resourcesFn({
99+
...resourcesFnArgs,
100+
params: {
101+
...resourcesFnArgs?.params,
102+
page,
103+
limit: constants.DEFAULT_LIMIT,
104+
},
105+
});
106+
107+
const nextResources = utils.getNestedProperty(response, resourceName);
108+
109+
if (!nextResources?.length) {
110+
console.log("No more resources found");
111+
return;
112+
}
113+
114+
for (const resource of nextResources) {
115+
const isDateGreater =
116+
lastDateAt
117+
&& Date.parse(resource[dateField]) >= Date.parse(lastDateAt);
118+
119+
if (!lastDateAt || isDateGreater) {
120+
yield resource;
121+
resourcesCount += 1;
122+
}
123+
124+
if (resourcesCount >= max) {
125+
console.log("Reached max resources");
126+
return;
127+
}
128+
}
129+
130+
if (nextResources.length < constants.DEFAULT_LIMIT) {
131+
console.log("No next page found");
132+
return;
133+
}
134+
135+
page += 1;
136+
}
137+
},
138+
paginate(args = {}) {
139+
return utils.iterate(this.getIterations(args));
9140
},
10141
},
11142
};

components/langfuse/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pipedream/langfuse",
3-
"version": "0.0.1",
3+
"version": "0.1.0",
44
"description": "Pipedream Langfuse Components",
55
"main": "langfuse.app.mjs",
66
"keywords": [
@@ -11,5 +11,8 @@
1111
"author": "Pipedream <[email protected]> (https://pipedream.com/)",
1212
"publishConfig": {
1313
"access": "public"
14+
},
15+
"dependencies": {
16+
"@pipedream/platform": "^3.0.3"
1417
}
1518
}

0 commit comments

Comments
 (0)