Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
daf4e2fe02 | ||
|
|
129693fb69 | ||
|
|
204948db44 | ||
|
|
d42ba5fc99 | ||
|
|
97a539ced3 | ||
|
|
9d8dace197 | ||
|
|
bc2dc4d89c |
@@ -22,6 +22,13 @@ service:
|
||||
docs: limit of items per page
|
||||
response: PaginatedAnnotationQueues
|
||||
|
||||
createQueue:
|
||||
docs: Create an annotation queue
|
||||
method: POST
|
||||
path: /annotation-queues
|
||||
request: CreateAnnotationQueueRequest
|
||||
response: AnnotationQueue
|
||||
|
||||
getQueue:
|
||||
docs: Get an annotation queue by ID
|
||||
method: GET
|
||||
@@ -168,6 +175,12 @@ types:
|
||||
data: list<AnnotationQueueItem>
|
||||
meta: pagination.MetaResponse
|
||||
|
||||
CreateAnnotationQueueRequest:
|
||||
properties:
|
||||
name: string
|
||||
description: optional<string>
|
||||
scoreConfigIds: list<string>
|
||||
|
||||
CreateAnnotationQueueItemRequest:
|
||||
properties:
|
||||
objectId: string
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "langfuse",
|
||||
"version": "3.97.2",
|
||||
"version": "3.97.3",
|
||||
"author": "engineering@langfuse.com",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
|
||||
@@ -158,6 +158,12 @@ const EnvSchema = z.object({
|
||||
LANGFUSE_EXPERIMENT_INSERT_INTO_AGGREGATING_MERGE_TREES: z
|
||||
.enum(["true", "false"])
|
||||
.default("false"),
|
||||
LANGFUSE_EXPERIMENT_WHITELISTED_AMT_TABLES: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((s) =>
|
||||
s ? s.split(",").map((s) => s.toLowerCase().trim()) : [],
|
||||
),
|
||||
LANGFUSE_INGESTION_PROCESSING_SAMPLED_PROJECTS: z
|
||||
.string()
|
||||
.optional()
|
||||
|
||||
@@ -47,12 +47,16 @@ enum TracesAMTs {
|
||||
* for <= 29 days, we use traces_30d_amt,
|
||||
* for all other cases we use traces_all_amt.
|
||||
*
|
||||
* If LANGFUSE_EXPERIMENT_WHITELISTED_AMT_TABLES is set, we only return timeframes
|
||||
* that are whitelisted or fallback to the traces_all_amt.
|
||||
*
|
||||
* @param fromTimestamp
|
||||
*/
|
||||
export const getTimeframesTracesAMT = (
|
||||
fromTimestamp: Date | undefined,
|
||||
): TracesAMTs => {
|
||||
if (!fromTimestamp) {
|
||||
// The TracesAllAMT must always be returned if there is no timestamp.
|
||||
return TracesAMTs.TracesAllAMT;
|
||||
}
|
||||
|
||||
@@ -60,12 +64,21 @@ export const getTimeframesTracesAMT = (
|
||||
const diffInDays = Math.floor(
|
||||
(now.getTime() - fromTimestamp.getTime()) / (1000 * 60 * 60 * 24),
|
||||
);
|
||||
|
||||
let selectedTable: TracesAMTs;
|
||||
if (diffInDays <= 6) {
|
||||
return TracesAMTs.Traces7dAMT;
|
||||
selectedTable = TracesAMTs.Traces7dAMT;
|
||||
} else if (diffInDays <= 29) {
|
||||
return TracesAMTs.Traces30dAMT;
|
||||
selectedTable = TracesAMTs.Traces30dAMT;
|
||||
} else {
|
||||
selectedTable = TracesAMTs.TracesAllAMT;
|
||||
}
|
||||
return TracesAMTs.TracesAllAMT;
|
||||
|
||||
// Check if the selected table is whitelisted, fallback to TracesAllAMT if not
|
||||
return env.LANGFUSE_EXPERIMENT_WHITELISTED_AMT_TABLES.length === 0 ||
|
||||
env.LANGFUSE_EXPERIMENT_WHITELISTED_AMT_TABLES.includes(selectedTable)
|
||||
? selectedTable
|
||||
: TracesAMTs.TracesAllAMT;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@ export function createBasicAuthHeader(
|
||||
|
||||
export type CreateOrgProjectAndApiKeyOptions = {
|
||||
projectId?: string;
|
||||
plan?: "Team" | "Hobby" | "Core" | "Pro" | "Enterprise";
|
||||
};
|
||||
export const createOrgProjectAndApiKey = async (
|
||||
props?: CreateOrgProjectAndApiKeyOptions,
|
||||
@@ -25,7 +26,7 @@ export const createOrgProjectAndApiKey = async (
|
||||
id: v4(),
|
||||
name: v4(),
|
||||
cloudConfig: CloudConfigSchema.parse({
|
||||
plan: "Team",
|
||||
plan: props?.plan ?? "Team",
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "web",
|
||||
"version": "3.97.2",
|
||||
"version": "3.97.3",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
||||
@@ -79,6 +79,52 @@ paths:
|
||||
schema: {}
|
||||
security:
|
||||
- BasicAuth: []
|
||||
post:
|
||||
description: Create an annotation queue
|
||||
operationId: annotationQueues_createQueue
|
||||
tags:
|
||||
- AnnotationQueues
|
||||
parameters: []
|
||||
responses:
|
||||
'200':
|
||||
description: ''
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/AnnotationQueue'
|
||||
'400':
|
||||
description: ''
|
||||
content:
|
||||
application/json:
|
||||
schema: {}
|
||||
'401':
|
||||
description: ''
|
||||
content:
|
||||
application/json:
|
||||
schema: {}
|
||||
'403':
|
||||
description: ''
|
||||
content:
|
||||
application/json:
|
||||
schema: {}
|
||||
'404':
|
||||
description: ''
|
||||
content:
|
||||
application/json:
|
||||
schema: {}
|
||||
'405':
|
||||
description: ''
|
||||
content:
|
||||
application/json:
|
||||
schema: {}
|
||||
security:
|
||||
- BasicAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CreateAnnotationQueueRequest'
|
||||
/api/public/annotation-queues/{queueId}:
|
||||
get:
|
||||
description: Get an annotation queue by ID
|
||||
@@ -3254,7 +3300,7 @@ paths:
|
||||
parameters:
|
||||
- name: filter
|
||||
in: query
|
||||
description: Filter expression (e.g. userName eq 'value')
|
||||
description: Filter expression (e.g. userName eq "value")
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
@@ -4459,6 +4505,22 @@ components:
|
||||
required:
|
||||
- data
|
||||
- meta
|
||||
CreateAnnotationQueueRequest:
|
||||
title: CreateAnnotationQueueRequest
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
nullable: true
|
||||
scoreConfigIds:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- scoreConfigIds
|
||||
CreateAnnotationQueueItemRequest:
|
||||
title: CreateAnnotationQueueItemRequest
|
||||
type: object
|
||||
|
||||
@@ -78,6 +78,39 @@
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"_type": "endpoint",
|
||||
"name": "Create Queue",
|
||||
"request": {
|
||||
"description": "Create an annotation queue",
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/api/public/annotation-queues",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"public",
|
||||
"annotation-queues"
|
||||
],
|
||||
"query": [],
|
||||
"variable": []
|
||||
},
|
||||
"header": [],
|
||||
"method": "POST",
|
||||
"auth": null,
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"name\": \"example\",\n \"description\": \"example\",\n \"scoreConfigIds\": [\n \"example\"\n ]\n}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"_type": "endpoint",
|
||||
"name": "Get Queue",
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
CreateAnnotationQueueItemResponse,
|
||||
UpdateAnnotationQueueItemResponse,
|
||||
DeleteAnnotationQueueItemResponse,
|
||||
CreateAnnotationQueueResponse,
|
||||
} from "@/src/features/public-api/types/annotation-queues";
|
||||
import {
|
||||
AnnotationQueueObjectType,
|
||||
@@ -173,6 +174,120 @@ describe("Annotation Queues API Endpoints", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("POST /annotation-queues", () => {
|
||||
it("should create a new annotation queue", async () => {
|
||||
const scoreConfig = await prisma.scoreConfig.create({
|
||||
data: {
|
||||
name: "Test Score Config",
|
||||
description: "Test Score Config Description",
|
||||
projectId,
|
||||
dataType: "NUMERIC",
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeZodVerifiedAPICall(
|
||||
CreateAnnotationQueueResponse,
|
||||
"POST",
|
||||
"/api/public/annotation-queues",
|
||||
{
|
||||
name: "Test Queue",
|
||||
description: "Test Queue Description",
|
||||
scoreConfigIds: [scoreConfig.id],
|
||||
},
|
||||
auth,
|
||||
);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.id).toBeDefined();
|
||||
expect(response.body.name).toBe("Test Queue");
|
||||
expect(response.body.description).toBe("Test Queue Description");
|
||||
expect(response.body.scoreConfigIds).toEqual([scoreConfig.id]);
|
||||
});
|
||||
|
||||
it("should return 400 if the queue name already exists", async () => {
|
||||
const response = await makeAPICall(
|
||||
"POST",
|
||||
"/api/public/annotation-queues",
|
||||
{
|
||||
name: "Test Queue",
|
||||
description: "Test Queue Description",
|
||||
scoreConfigIds: [],
|
||||
},
|
||||
auth,
|
||||
);
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
});
|
||||
|
||||
it("should return 400 if no score config IDs are provided", async () => {
|
||||
const response = await makeAPICall(
|
||||
"POST",
|
||||
"/api/public/annotation-queues",
|
||||
{
|
||||
name: "No configs queue",
|
||||
description: "Test Queue Description",
|
||||
scoreConfigIds: [],
|
||||
},
|
||||
auth,
|
||||
);
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
});
|
||||
|
||||
it("should return 400 if the score config IDs are invalid", async () => {
|
||||
const response = await makeAPICall(
|
||||
"POST",
|
||||
"/api/public/annotation-queues",
|
||||
{
|
||||
name: "Invalid configs queue",
|
||||
description: "Test Queue Description",
|
||||
scoreConfigIds: ["invalid-score-config-id"],
|
||||
},
|
||||
auth,
|
||||
);
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
});
|
||||
|
||||
it("should return 405 if the user is on the Hobby plan and has reached the maximum number of annotation queues", async () => {
|
||||
const { auth: hobbyPlanAuth, projectId: hobbyProjectId } =
|
||||
await createOrgProjectAndApiKey({
|
||||
plan: "Hobby",
|
||||
});
|
||||
|
||||
const config = await prisma.scoreConfig.create({
|
||||
data: {
|
||||
name: "Test Score Config",
|
||||
description: "Test Score Config Description",
|
||||
projectId: hobbyProjectId,
|
||||
dataType: "NUMERIC",
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.annotationQueue.create({
|
||||
data: {
|
||||
name: "First queue",
|
||||
description: "First queue description",
|
||||
scoreConfigIds: [config.id],
|
||||
projectId: hobbyProjectId,
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeAPICall(
|
||||
"POST",
|
||||
"/api/public/annotation-queues",
|
||||
{
|
||||
name: "Hobby plan queue",
|
||||
description: "Test Queue Description",
|
||||
scoreConfigIds: [config.id],
|
||||
},
|
||||
hobbyPlanAuth,
|
||||
);
|
||||
|
||||
expect(response.status).toBe(405);
|
||||
});
|
||||
});
|
||||
|
||||
describe("GET /annotation-queues/:queueId", () => {
|
||||
it("should get a specific annotation queue", async () => {
|
||||
const response = await makeZodVerifiedAPICall(
|
||||
|
||||
@@ -1 +1 @@
|
||||
export const VERSION = "v3.97.2";
|
||||
export const VERSION = "v3.97.3";
|
||||
|
||||
@@ -33,9 +33,6 @@ export const EvalTemplateDetail = () => {
|
||||
const templateId = router.query.id as string;
|
||||
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [selectedTemplate, setSelectedTemplate] = useState<EvalTemplate | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
// get the current template by id
|
||||
const template = api.evals.templateById.useQuery({
|
||||
@@ -58,15 +55,7 @@ export const EvalTemplateDetail = () => {
|
||||
},
|
||||
);
|
||||
|
||||
// Set the selected template when data is loaded
|
||||
React.useEffect(() => {
|
||||
if (template.data && !selectedTemplate) {
|
||||
setSelectedTemplate(template.data);
|
||||
}
|
||||
}, [template.data, selectedTemplate]);
|
||||
|
||||
const handleTemplateSelect = (newTemplate: EvalTemplate) => {
|
||||
setSelectedTemplate(newTemplate);
|
||||
// Update URL without full page reload
|
||||
router.push(
|
||||
`/project/${projectId}/evals/templates/${newTemplate.id}`,
|
||||
@@ -75,13 +64,10 @@ export const EvalTemplateDetail = () => {
|
||||
);
|
||||
};
|
||||
|
||||
// Get the appropriate template to display
|
||||
const displayTemplate = selectedTemplate || template.data;
|
||||
|
||||
return (
|
||||
<Page
|
||||
headerProps={{
|
||||
title: `${displayTemplate?.name || ""}`,
|
||||
title: `${template.data?.name ?? ""}`,
|
||||
itemType: "EVALUATOR",
|
||||
breadcrumb: [
|
||||
{
|
||||
@@ -95,7 +81,7 @@ export const EvalTemplateDetail = () => {
|
||||
projectId={projectId}
|
||||
isEditing={isEditing}
|
||||
setIsEditing={setIsEditing}
|
||||
isCustom={!!displayTemplate?.projectId}
|
||||
isCustom={!!template.data?.projectId}
|
||||
/>
|
||||
|
||||
{/* TODO: moved to LFE-4573 */}
|
||||
@@ -114,14 +100,14 @@ export const EvalTemplateDetail = () => {
|
||||
),
|
||||
}}
|
||||
>
|
||||
{allTemplates.isLoading || !allTemplates.data || !displayTemplate ? (
|
||||
{allTemplates.isLoading || !allTemplates.data || !template.data ? (
|
||||
<div className="p-3">Loading...</div>
|
||||
) : isEditing ? (
|
||||
<div className="overflow-y-auto p-3 pt-1">
|
||||
<EvalTemplateForm
|
||||
useDialog={false}
|
||||
projectId={projectId}
|
||||
existingEvalTemplate={displayTemplate}
|
||||
existingEvalTemplate={template.data}
|
||||
isEditing={isEditing}
|
||||
setIsEditing={setIsEditing}
|
||||
/>
|
||||
@@ -132,7 +118,7 @@ export const EvalTemplateDetail = () => {
|
||||
<EvalTemplateForm
|
||||
useDialog={false}
|
||||
projectId={projectId}
|
||||
existingEvalTemplate={displayTemplate}
|
||||
existingEvalTemplate={template.data}
|
||||
isEditing={isEditing}
|
||||
setIsEditing={setIsEditing}
|
||||
/>
|
||||
@@ -150,7 +136,7 @@ export const EvalTemplateDetail = () => {
|
||||
<div
|
||||
key={template.id}
|
||||
className={`flex cursor-pointer flex-col rounded-md px-2 py-1.5 hover:bg-accent ${
|
||||
template.id === displayTemplate.id ? "bg-accent" : ""
|
||||
template.id === templateId ? "bg-accent" : ""
|
||||
}`}
|
||||
onClick={() => handleTemplateSelect(template)}
|
||||
>
|
||||
|
||||
@@ -58,6 +58,12 @@ export function EvaluatorSelector({
|
||||
},
|
||||
);
|
||||
|
||||
// Ensure per-name arrays are sorted by createdAt ascending so last is latest
|
||||
const sortByCreatedAt = (arr: EvalTemplate[]) =>
|
||||
arr.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
|
||||
Object.values(groupedTemplates.custom).forEach(sortByCreatedAt);
|
||||
Object.values(groupedTemplates.langfuse).forEach(sortByCreatedAt);
|
||||
|
||||
// Filter templates based on search
|
||||
const filteredTemplates = {
|
||||
langfuse: Object.entries(groupedTemplates.langfuse)
|
||||
|
||||
@@ -289,6 +289,7 @@ export const llmApiKeyRouter = createTRPCRouter({
|
||||
customModels: true,
|
||||
withDefaultModels: true,
|
||||
extraHeaderKeys: true,
|
||||
config: true,
|
||||
},
|
||||
where: {
|
||||
projectId: input.projectId,
|
||||
|
||||
@@ -84,10 +84,9 @@ export const generateTracesForPublicApi = async ({
|
||||
SELECT
|
||||
trace_id,
|
||||
project_id,
|
||||
sum(total_cost) as total_cost,
|
||||
date_diff('millisecond', least(min(start_time), min(end_time)), greatest(max(start_time), max(end_time))) as latency_milliseconds,
|
||||
groupArray(id) as observation_ids
|
||||
FROM observations FINAL
|
||||
${includeMetrics ? "sum(total_cost) as total_cost, date_diff('millisecond', least(min(start_time), min(end_time)), greatest(max(start_time), max(end_time))) as latency_milliseconds, " : ""}
|
||||
groupUniqArray(id) as observation_ids
|
||||
FROM observations ${includeMetrics ? "FINAL" : ""}
|
||||
WHERE project_id = {projectId: String}
|
||||
${timeFilter ? `AND start_time >= {cteTimeFilter: DateTime64(3)} - ${TRACE_TO_OBSERVATIONS_INTERVAL}` : ""}
|
||||
${environmentFilter.length() > 0 ? `AND ${appliedEnvironmentFilter.query}` : ""}
|
||||
|
||||
@@ -57,6 +57,17 @@ export const GetAnnotationQueuesResponse = z
|
||||
})
|
||||
.strict();
|
||||
|
||||
// POST /annotation-queues
|
||||
export const CreateAnnotationQueueBody = z
|
||||
.object({
|
||||
name: z.string(),
|
||||
description: z.string().nullable(),
|
||||
scoreConfigIds: z.array(z.string()).min(1),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const CreateAnnotationQueueResponse = AnnotationQueueSchema;
|
||||
|
||||
// GET /annotation-queues/:queueId
|
||||
export const GetAnnotationQueueByIdQuery = z
|
||||
.object({
|
||||
|
||||
@@ -56,13 +56,6 @@ export default async function handler(
|
||||
return;
|
||||
}
|
||||
|
||||
const body = ManageBullBody.safeParse(req.body);
|
||||
|
||||
if (!body.success) {
|
||||
res.status(400).json({ error: body.error });
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === "GET") {
|
||||
const queues: string[] = Object.values(QueueName);
|
||||
queues.push(...IngestionQueue.getShardNames());
|
||||
@@ -94,6 +87,13 @@ export default async function handler(
|
||||
return res.status(200).json(queueCounts);
|
||||
}
|
||||
|
||||
const body = ManageBullBody.safeParse(req.body);
|
||||
|
||||
if (!body.success) {
|
||||
res.status(400).json({ error: body.error });
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === "POST" && body.data.action === "remove") {
|
||||
logger.info(
|
||||
`Removing jobs for queues ${body.data.queueNames.join(", ")}`,
|
||||
|
||||
@@ -2,12 +2,14 @@ import { prisma } from "@langfuse/shared/src/db";
|
||||
import { withMiddlewares } from "@/src/features/public-api/server/withMiddlewares";
|
||||
import { createAuthedProjectAPIRoute } from "@/src/features/public-api/server/createAuthedProjectAPIRoute";
|
||||
import {
|
||||
CreateAnnotationQueueBody,
|
||||
CreateAnnotationQueueResponse,
|
||||
GetAnnotationQueuesQuery,
|
||||
GetAnnotationQueuesResponse,
|
||||
} from "@/src/features/public-api/types/annotation-queues";
|
||||
import { InvalidRequestError, MethodNotAllowedError } from "@langfuse/shared";
|
||||
|
||||
export default withMiddlewares({
|
||||
// NOTE: Post API requires entitlement check
|
||||
GET: createAuthedProjectAPIRoute({
|
||||
name: "Get annotation queues",
|
||||
querySchema: GetAnnotationQueuesQuery,
|
||||
@@ -54,4 +56,72 @@ export default withMiddlewares({
|
||||
};
|
||||
},
|
||||
}),
|
||||
|
||||
POST: createAuthedProjectAPIRoute({
|
||||
name: "Create annotation queue",
|
||||
bodySchema: CreateAnnotationQueueBody,
|
||||
responseSchema: CreateAnnotationQueueResponse,
|
||||
fn: async ({ body, auth }) => {
|
||||
// entitlement check
|
||||
if (auth.scope.plan === "cloud:hobby") {
|
||||
if (
|
||||
(await prisma.annotationQueue.count({
|
||||
where: {
|
||||
projectId: auth.scope.projectId,
|
||||
},
|
||||
})) >= 1
|
||||
) {
|
||||
throw new MethodNotAllowedError(
|
||||
"Maximum number of annotation queues reached on Hobby plan.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const existingQueue = await prisma.annotationQueue.findFirst({
|
||||
where: {
|
||||
projectId: auth.scope.projectId,
|
||||
name: body.name,
|
||||
},
|
||||
});
|
||||
|
||||
if (existingQueue) {
|
||||
throw new InvalidRequestError("A queue with this name already exists.");
|
||||
}
|
||||
|
||||
// verify the score configs exist
|
||||
const scoreConfigs = await prisma.scoreConfig.findMany({
|
||||
where: {
|
||||
id: { in: body.scoreConfigIds },
|
||||
projectId: auth.scope.projectId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
const scoreConfigIdSet = new Set(scoreConfigs.map((config) => config.id));
|
||||
if (body.scoreConfigIds.some((id) => !scoreConfigIdSet.has(id))) {
|
||||
throw new InvalidRequestError(
|
||||
"At least one of the score config IDs cannot be found for the given project.",
|
||||
);
|
||||
}
|
||||
|
||||
const queue = await prisma.annotationQueue.create({
|
||||
data: {
|
||||
projectId: auth.scope.projectId,
|
||||
name: body.name,
|
||||
description: body.description,
|
||||
scoreConfigIds: body.scoreConfigIds,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
id: queue.id,
|
||||
name: queue.name,
|
||||
description: queue.description,
|
||||
scoreConfigIds: queue.scoreConfigIds,
|
||||
createdAt: queue.createdAt,
|
||||
updatedAt: queue.updatedAt,
|
||||
};
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "worker",
|
||||
"version": "3.97.2",
|
||||
"version": "3.97.3",
|
||||
"description": "",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
|
||||
@@ -1 +1 @@
|
||||
export const VERSION = "v3.97.2";
|
||||
export const VERSION = "v3.97.3";
|
||||
|
||||
Reference in New Issue
Block a user