feat(server): add copilot prompts management api (#7082)
This commit is contained in:
@@ -15,7 +15,11 @@ import {
|
|||||||
OpenAIProvider,
|
OpenAIProvider,
|
||||||
registerCopilotProvider,
|
registerCopilotProvider,
|
||||||
} from './providers';
|
} from './providers';
|
||||||
import { CopilotResolver, UserCopilotResolver } from './resolver';
|
import {
|
||||||
|
CopilotResolver,
|
||||||
|
PromptsManagementResolver,
|
||||||
|
UserCopilotResolver,
|
||||||
|
} from './resolver';
|
||||||
import { ChatSessionService } from './session';
|
import { ChatSessionService } from './session';
|
||||||
import { CopilotStorage } from './storage';
|
import { CopilotStorage } from './storage';
|
||||||
|
|
||||||
@@ -34,6 +38,7 @@ registerCopilotProvider(OpenAIProvider);
|
|||||||
PromptService,
|
PromptService,
|
||||||
CopilotProviderService,
|
CopilotProviderService,
|
||||||
CopilotStorage,
|
CopilotStorage,
|
||||||
|
PromptsManagementResolver,
|
||||||
],
|
],
|
||||||
controllers: [CopilotController],
|
controllers: [CopilotController],
|
||||||
contributesTo: ServerFeature.Copilot,
|
contributesTo: ServerFeature.Copilot,
|
||||||
|
|||||||
@@ -142,12 +142,32 @@ export class PromptService {
|
|||||||
* list prompt names
|
* list prompt names
|
||||||
* @returns prompt names
|
* @returns prompt names
|
||||||
*/
|
*/
|
||||||
async list() {
|
async listNames() {
|
||||||
return this.db.aiPrompt
|
return this.db.aiPrompt
|
||||||
.findMany({ select: { name: true } })
|
.findMany({ select: { name: true } })
|
||||||
.then(prompts => Array.from(new Set(prompts.map(p => p.name))));
|
.then(prompts => Array.from(new Set(prompts.map(p => p.name))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async list() {
|
||||||
|
return this.db.aiPrompt.findMany({
|
||||||
|
select: {
|
||||||
|
name: true,
|
||||||
|
action: true,
|
||||||
|
model: true,
|
||||||
|
messages: {
|
||||||
|
select: {
|
||||||
|
role: true,
|
||||||
|
content: true,
|
||||||
|
params: true,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
idx: 'asc',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get prompt messages by prompt name
|
* get prompt messages by prompt name
|
||||||
* @param name prompt name
|
* @param name prompt name
|
||||||
|
|||||||
@@ -9,14 +9,17 @@ import {
|
|||||||
Mutation,
|
Mutation,
|
||||||
ObjectType,
|
ObjectType,
|
||||||
Parent,
|
Parent,
|
||||||
|
Query,
|
||||||
registerEnumType,
|
registerEnumType,
|
||||||
ResolveField,
|
ResolveField,
|
||||||
Resolver,
|
Resolver,
|
||||||
} from '@nestjs/graphql';
|
} from '@nestjs/graphql';
|
||||||
|
import { AiPromptRole } from '@prisma/client';
|
||||||
import { GraphQLJSON, SafeIntResolver } from 'graphql-scalars';
|
import { GraphQLJSON, SafeIntResolver } from 'graphql-scalars';
|
||||||
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs';
|
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs';
|
||||||
|
|
||||||
import { CurrentUser } from '../../core/auth';
|
import { CurrentUser } from '../../core/auth';
|
||||||
|
import { Admin } from '../../core/common';
|
||||||
import { UserType } from '../../core/user';
|
import { UserType } from '../../core/user';
|
||||||
import { PermissionService } from '../../core/workspaces/permission';
|
import { PermissionService } from '../../core/workspaces/permission';
|
||||||
import {
|
import {
|
||||||
@@ -25,6 +28,7 @@ import {
|
|||||||
Throttle,
|
Throttle,
|
||||||
TooManyRequestsException,
|
TooManyRequestsException,
|
||||||
} from '../../fundamentals';
|
} from '../../fundamentals';
|
||||||
|
import { PromptService } from './prompt';
|
||||||
import { ChatSessionService } from './session';
|
import { ChatSessionService } from './session';
|
||||||
import { CopilotStorage } from './storage';
|
import { CopilotStorage } from './storage';
|
||||||
import {
|
import {
|
||||||
@@ -152,6 +156,40 @@ class CopilotQuotaType {
|
|||||||
used!: number;
|
used!: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerEnumType(AiPromptRole, {
|
||||||
|
name: 'CopilotPromptMessageRole',
|
||||||
|
});
|
||||||
|
|
||||||
|
@InputType('CopilotPromptMessageInput')
|
||||||
|
@ObjectType()
|
||||||
|
class CopilotPromptMessageType {
|
||||||
|
@Field(() => AiPromptRole)
|
||||||
|
role!: AiPromptRole;
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
content!: string;
|
||||||
|
|
||||||
|
@Field(() => GraphQLJSON, { nullable: true })
|
||||||
|
params!: Record<string, string> | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerEnumType(AvailableModels, { name: 'CopilotModels' });
|
||||||
|
|
||||||
|
@ObjectType()
|
||||||
|
class CopilotPromptType {
|
||||||
|
@Field(() => String)
|
||||||
|
name!: string;
|
||||||
|
|
||||||
|
@Field(() => AvailableModels)
|
||||||
|
model!: AvailableModels;
|
||||||
|
|
||||||
|
@Field(() => String, { nullable: true })
|
||||||
|
action!: string | null;
|
||||||
|
|
||||||
|
@Field(() => [CopilotPromptMessageType])
|
||||||
|
messages!: CopilotPromptMessageType[];
|
||||||
|
}
|
||||||
|
|
||||||
// ================== Resolver ==================
|
// ================== Resolver ==================
|
||||||
|
|
||||||
@ObjectType('Copilot')
|
@ObjectType('Copilot')
|
||||||
@@ -370,3 +408,54 @@ export class UserCopilotResolver {
|
|||||||
return { workspaceId };
|
return { workspaceId };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@InputType()
|
||||||
|
class CreateCopilotPromptInput {
|
||||||
|
@Field(() => String)
|
||||||
|
name!: string;
|
||||||
|
|
||||||
|
@Field(() => AvailableModels)
|
||||||
|
model!: AvailableModels;
|
||||||
|
|
||||||
|
@Field(() => String, { nullable: true })
|
||||||
|
action!: string | null;
|
||||||
|
|
||||||
|
@Field(() => [CopilotPromptMessageType])
|
||||||
|
messages!: CopilotPromptMessageType[];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Admin()
|
||||||
|
@Resolver(() => String)
|
||||||
|
export class PromptsManagementResolver {
|
||||||
|
constructor(private readonly promptService: PromptService) {}
|
||||||
|
|
||||||
|
@Query(() => [CopilotPromptType], {
|
||||||
|
description: 'List all copilot prompts',
|
||||||
|
})
|
||||||
|
async listCopilotPrompts() {
|
||||||
|
return this.promptService.list();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mutation(() => CopilotPromptType, {
|
||||||
|
description: 'Create a copilot prompt',
|
||||||
|
})
|
||||||
|
async createCopilotPrompt(
|
||||||
|
@Args({ type: () => CreateCopilotPromptInput, name: 'input' })
|
||||||
|
input: CreateCopilotPromptInput
|
||||||
|
) {
|
||||||
|
await this.promptService.set(input.name, input.model, input.messages);
|
||||||
|
return this.promptService.get(input.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mutation(() => CopilotPromptType, {
|
||||||
|
description: 'Update a copilot prompt',
|
||||||
|
})
|
||||||
|
async updateCopilotPrompt(
|
||||||
|
@Args('name') name: string,
|
||||||
|
@Args('messages', { type: () => [CopilotPromptMessageType] })
|
||||||
|
messages: CopilotPromptMessageType[]
|
||||||
|
) {
|
||||||
|
await this.promptService.update(name, messages);
|
||||||
|
return this.promptService.get(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -34,6 +34,44 @@ type CopilotHistories {
|
|||||||
tokens: Int!
|
tokens: Int!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum CopilotModels {
|
||||||
|
DallE3
|
||||||
|
Gpt4Omni
|
||||||
|
Gpt4TurboPreview
|
||||||
|
Gpt4VisionPreview
|
||||||
|
Gpt35Turbo
|
||||||
|
TextEmbedding3Large
|
||||||
|
TextEmbedding3Small
|
||||||
|
TextEmbeddingAda002
|
||||||
|
TextModerationLatest
|
||||||
|
TextModerationStable
|
||||||
|
}
|
||||||
|
|
||||||
|
input CopilotPromptMessageInput {
|
||||||
|
content: String!
|
||||||
|
params: JSON
|
||||||
|
role: CopilotPromptMessageRole!
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CopilotPromptMessageRole {
|
||||||
|
assistant
|
||||||
|
system
|
||||||
|
user
|
||||||
|
}
|
||||||
|
|
||||||
|
type CopilotPromptMessageType {
|
||||||
|
content: String!
|
||||||
|
params: JSON
|
||||||
|
role: CopilotPromptMessageRole!
|
||||||
|
}
|
||||||
|
|
||||||
|
type CopilotPromptType {
|
||||||
|
action: String
|
||||||
|
messages: [CopilotPromptMessageType!]!
|
||||||
|
model: CopilotModels!
|
||||||
|
name: String!
|
||||||
|
}
|
||||||
|
|
||||||
type CopilotQuota {
|
type CopilotQuota {
|
||||||
limit: SafeInt
|
limit: SafeInt
|
||||||
used: SafeInt!
|
used: SafeInt!
|
||||||
@@ -63,6 +101,13 @@ input CreateCheckoutSessionInput {
|
|||||||
successCallbackLink: String!
|
successCallbackLink: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input CreateCopilotPromptInput {
|
||||||
|
action: String
|
||||||
|
messages: [CopilotPromptMessageInput!]!
|
||||||
|
model: CopilotModels!
|
||||||
|
name: String!
|
||||||
|
}
|
||||||
|
|
||||||
type CredentialsRequirementType {
|
type CredentialsRequirementType {
|
||||||
password: PasswordLimitsType!
|
password: PasswordLimitsType!
|
||||||
}
|
}
|
||||||
@@ -206,6 +251,9 @@ type Mutation {
|
|||||||
"""Create a chat message"""
|
"""Create a chat message"""
|
||||||
createCopilotMessage(options: CreateChatMessageInput!): String!
|
createCopilotMessage(options: CreateChatMessageInput!): String!
|
||||||
|
|
||||||
|
"""Create a copilot prompt"""
|
||||||
|
createCopilotPrompt(input: CreateCopilotPromptInput!): CopilotPromptType!
|
||||||
|
|
||||||
"""Create a chat session"""
|
"""Create a chat session"""
|
||||||
createCopilotSession(options: CreateChatSessionInput!): String!
|
createCopilotSession(options: CreateChatSessionInput!): String!
|
||||||
|
|
||||||
@@ -238,6 +286,9 @@ type Mutation {
|
|||||||
setBlob(blob: Upload!, workspaceId: String!): String!
|
setBlob(blob: Upload!, workspaceId: String!): String!
|
||||||
setWorkspaceExperimentalFeature(enable: Boolean!, feature: FeatureType!, workspaceId: String!): Boolean!
|
setWorkspaceExperimentalFeature(enable: Boolean!, feature: FeatureType!, workspaceId: String!): Boolean!
|
||||||
sharePage(pageId: String!, workspaceId: String!): Boolean! @deprecated(reason: "renamed to publishPage")
|
sharePage(pageId: String!, workspaceId: String!): Boolean! @deprecated(reason: "renamed to publishPage")
|
||||||
|
|
||||||
|
"""Update a copilot prompt"""
|
||||||
|
updateCopilotPrompt(messages: [CopilotPromptMessageInput!]!, name: String!): CopilotPromptType!
|
||||||
updateProfile(input: UpdateUserInput!): UserType!
|
updateProfile(input: UpdateUserInput!): UserType!
|
||||||
|
|
||||||
"""update server runtime configurable setting"""
|
"""update server runtime configurable setting"""
|
||||||
@@ -296,6 +347,9 @@ type Query {
|
|||||||
|
|
||||||
"""List blobs of workspace"""
|
"""List blobs of workspace"""
|
||||||
listBlobs(workspaceId: String!): [String!]! @deprecated(reason: "use `workspace.blobs` instead")
|
listBlobs(workspaceId: String!): [String!]! @deprecated(reason: "use `workspace.blobs` instead")
|
||||||
|
|
||||||
|
"""List all copilot prompts"""
|
||||||
|
listCopilotPrompts: [CopilotPromptType!]!
|
||||||
listWorkspaceFeatures(feature: FeatureType!): [WorkspaceType!]!
|
listWorkspaceFeatures(feature: FeatureType!): [WorkspaceType!]!
|
||||||
prices: [SubscriptionPrice!]!
|
prices: [SubscriptionPrice!]!
|
||||||
|
|
||||||
|
|||||||
@@ -77,13 +77,13 @@ test.beforeEach(async t => {
|
|||||||
test('should be able to manage prompt', async t => {
|
test('should be able to manage prompt', async t => {
|
||||||
const { prompt } = t.context;
|
const { prompt } = t.context;
|
||||||
|
|
||||||
t.is((await prompt.list()).length, 0, 'should have no prompt');
|
t.is((await prompt.listNames()).length, 0, 'should have no prompt');
|
||||||
|
|
||||||
await prompt.set('test', 'test', [
|
await prompt.set('test', 'test', [
|
||||||
{ role: 'system', content: 'hello' },
|
{ role: 'system', content: 'hello' },
|
||||||
{ role: 'user', content: 'hello' },
|
{ role: 'user', content: 'hello' },
|
||||||
]);
|
]);
|
||||||
t.is((await prompt.list()).length, 1, 'should have one prompt');
|
t.is((await prompt.listNames()).length, 1, 'should have one prompt');
|
||||||
t.is(
|
t.is(
|
||||||
(await prompt.get('test'))!.finish({}).length,
|
(await prompt.get('test'))!.finish({}).length,
|
||||||
2,
|
2,
|
||||||
@@ -98,7 +98,7 @@ test('should be able to manage prompt', async t => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await prompt.delete('test');
|
await prompt.delete('test');
|
||||||
t.is((await prompt.list()).length, 0, 'should have no prompt');
|
t.is((await prompt.listNames()).length, 0, 'should have no prompt');
|
||||||
t.is(await prompt.get('test'), null, 'should not have the prompt');
|
t.is(await prompt.get('test'), null, 'should not have the prompt');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user