diff --git a/.eslintrc.json b/.eslintrc.json index ecbd3e1b..d690f6f8 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -26,7 +26,6 @@ "import/prefer-default-export": "off", "comma-dangle": ["error", "always-multiline"], "indent": "off", - "@typescript-eslint/indent": ["error", 4], "max-len": ["error", 140], "@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/prefer-optional-chain": "error", @@ -51,6 +50,7 @@ "devDependencies": true } ], - "import/no-cycle": "off" + "import/no-cycle": "off", + "@typescript-eslint/no-explicit-any": 1 } } diff --git a/clients/ai-assistants-v1/sdk.ts b/clients/ai-assistants-v1/sdk.ts new file mode 100644 index 00000000..1aef7d48 --- /dev/null +++ b/clients/ai-assistants-v1/sdk.ts @@ -0,0 +1,382 @@ +import { ServiceDefinition } from '@grpc/grpc-js'; +import { assistantService, messageService, runService, threadService } from '.'; +import { ClientCallArgs, GeneratedServiceClientCtor, WrappedServiceClientType } from './types'; +import { + CreateThreadRequest, + DeleteThreadRequest, + GetThreadRequest, + ListThreadsRequest, + UpdateThreadRequest, +} from './generated/yandex/cloud/ai/assistants/v1/threads/thread_service'; +import { + CreateAssistantRequest, + DeleteAssistantRequest, + GetAssistantRequest, + ListAssistantsRequest, + ListAssistantVersionsRequest, + UpdateAssistantRequest, +} from './generated/yandex/cloud/ai/assistants/v1/assistant_service'; +import { + CreateMessageRequest, + GetMessageRequest, + ListMessagesRequest, +} from './generated/yandex/cloud/ai/assistants/v1/threads/message_service'; +import { DeepPartial } from './generated/typeRegistry'; +import { + CreateRunRequest, + GetLastRunByThreadRequest, + GetRunRequest, + ListenRunRequest, + ListRunsRequest, +} from './generated/yandex/cloud/ai/assistants/v1/runs/run_service'; +import { CallOptions } from 'nice-grpc'; +import { Thread } from './generated/yandex/cloud/ai/assistants/v1/threads/thread'; +import { Assistant } from './generated/yandex/cloud/ai/assistants/v1/assistant'; +import { Run } from './generated/yandex/cloud/ai/assistants/v1/runs/run'; + +type ClientType = ( + clientClass: GeneratedServiceClientCtor, + customEndpoint?: string, +) => WrappedServiceClientType; + +type SessionArg = { client: ClientType }; + +type TypeFromProtoc< + T extends { $type: string }, + NotPartialKey extends keyof Omit = never, +> = { + [Key in NotPartialKey]: T[Key]; +} & DeepPartial; + +type SendMessageProps = TypeFromProtoc; + +type GetMessageProps = TypeFromProtoc; + +type ListMessagesProps = TypeFromProtoc; + +export const initMessageSdk = (session: SessionArg) => { + const messageClient = session.client(messageService.MessageServiceClient); + + const send = (params: SendMessageProps, args?: ClientCallArgs) => { + const p = messageClient.create( + messageService.CreateMessageRequest.fromPartial(params), + args, + ); + + return p; + }; + + const get = (params: GetMessageProps, args?: ClientCallArgs) => { + const p = messageClient.get(messageService.GetMessageRequest.fromPartial(params), args); + return p; + }; + + const list = (params: ListMessagesProps, args?: ClientCallArgs) => { + const p = messageClient.list(messageService.ListMessagesRequest.fromPartial(params), args); + return p; + }; + + return { send, get, list }; +}; + +type MessageSdk = ReturnType; + +type CreateThreadProps = TypeFromProtoc; + +type GetThreadProps = TypeFromProtoc; + +type ListThreadProps = TypeFromProtoc; + +type DeleteThreadProps = TypeFromProtoc; + +type UpdateThreadProps = TypeFromProtoc; + +export const threadWithSdk = >( + session: SessionArg, + thread: T, + { + threadSdk = initThreadSdk(session), + messageSdk = initMessageSdk(session), + }: { + messageSdk?: MessageSdk; + threadSdk?: ThreadSdk; + }, +) => { + const sendMessage = (props: Omit, args?: ClientCallArgs) => { + return messageSdk.send({ threadId: thread.id, ...props }, args); + }; + + const listMessages = (props: Omit, args?: ClientCallArgs) => { + return messageSdk.list({ threadId: thread.id, ...props }, args); + }; + + const getMessage = (props: Omit, args?: ClientCallArgs) => { + return messageSdk.get({ threadId: thread.id, ...props }, args); + }; + + const deleteInnerMethod = (args?: ClientCallArgs) => { + return threadSdk.delete({ threadId: thread.id }, args); + }; + + const updateMethod = (props: Omit, args?: ClientCallArgs) => { + return threadSdk.update({ ...props, threadId: thread.id }, args); + }; + + return Object.assign(thread, { + sendMessage, + listMessages, + getMessage, + delete: deleteInnerMethod, + update: updateMethod, + }); +}; + +export const initThreadSdk = (session: SessionArg) => { + const threadClient = session.client(threadService.ThreadServiceClient); + + const _connectWithSdk = (threadP: Promise) => { + const externalSession = session; + + const withSdk = async (session?: SessionArg | null, messageSdk?: MessageSdk) => { + const thread = await threadP; + return threadWithSdk(session || externalSession, thread, { + threadSdk, + messageSdk, + }); + }; + + return { withSdk }; + }; + + const get = (params: GetThreadProps, args?: ClientCallArgs) => { + const p = threadClient.get(threadService.GetThreadRequest.fromPartial(params), args); + return Object.assign(p, _connectWithSdk(p)); + }; + + const list = (params: ListThreadProps, args?: ClientCallArgs) => { + return threadClient.list(threadService.ListThreadsRequest.fromPartial(params), args); + }; + + const deleteMethod = (params: DeleteThreadProps, args?: ClientCallArgs) => { + return threadClient.delete(threadService.DeleteThreadRequest.fromPartial(params), args); + }; + + const update = (params: UpdateThreadProps, args?: ClientCallArgs) => { + const p = threadClient.update(threadService.UpdateThreadRequest.fromPartial(params), args); + return Object.assign(p, _connectWithSdk(p)); + }; + + const create = (params: CreateThreadProps, args?: ClientCallArgs) => { + const p = threadClient.create(threadService.CreateThreadRequest.fromPartial(params), args); + return Object.assign(p, _connectWithSdk(p)); + }; + + const threadSdk = { create, delete: deleteMethod, get, list, update }; + + return threadSdk; +}; + +type ThreadSdk = ReturnType; + +type CreateAssistantProps = Omit, 'modelUri'> & { + modelId: string; +}; + +type GetAssistantProps = TypeFromProtoc; + +type ListAssistantProps = TypeFromProtoc; + +type DeleteAssistantProps = TypeFromProtoc; + +type ListAssistantVersionsProps = TypeFromProtoc; + +type UpdateAssistantProps = TypeFromProtoc; + +export const assistantWithSdk = >( + session: SessionArg, + assistant: T, + { + assistantSdk = initAssistantSdk(session), + }: { + assistantSdk?: AssistantSdk; + }, +) => { + const deleteMethod = ( + params: Omit, + args?: ClientCallArgs, + ) => { + return assistantSdk.delete({ ...params, assistantId: assistant.id }, args); + }; + + const update = (params: Omit, args?: ClientCallArgs) => { + return assistantSdk.update({ ...params, assistantId: assistant.id }, args); + }; + + return Object.assign(assistant, { update, delete: deleteMethod }); +}; + +export const initAssistantSdk = (session: SessionArg) => { + const assistantClient = session.client(assistantService.AssistantServiceClient); + + const _connectWithSdk = (assistantP: Promise) => { + const externalSession = session; + + const withSdk = async (session?: SessionArg | null) => { + const thread = await assistantP; + return assistantWithSdk(session || externalSession, thread, { + assistantSdk, + }); + }; + + return { withSdk }; + }; + + const create = async (params: CreateAssistantProps, args?: ClientCallArgs) => { + const { modelId, ...restParams } = params; + + const p = assistantClient.create( + assistantService.CreateAssistantRequest.fromPartial({ + ...restParams, + modelUri: `gpt://${params.folderId}/${modelId}`, + }), + args, + ); + + return Object.assign(p, _connectWithSdk(p)); + }; + + const get = (params: GetAssistantProps, args?: ClientCallArgs) => { + const p = assistantClient.get( + assistantService.GetAssistantRequest.fromPartial(params), + args, + ); + return Object.assign(p, _connectWithSdk(p)); + }; + + const list = (params: ListAssistantProps, args?: ClientCallArgs) => { + const p = assistantClient.list( + assistantService.ListAssistantsRequest.fromPartial(params), + args, + ); + + return p; + }; + + const deleteMethod = (params: DeleteAssistantProps, args?: ClientCallArgs) => { + const p = assistantClient.delete( + assistantService.DeleteAssistantRequest.fromPartial(params), + args, + ); + return p; + }; + + const listVersions = (params: ListAssistantVersionsProps, args?: ClientCallArgs) => { + const p = assistantClient.listVersions( + assistantService.ListAssistantVersionsRequest.fromPartial(params), + args, + ); + return p; + }; + + const update = (params: UpdateAssistantProps, args?: ClientCallArgs) => { + const p = assistantClient.update( + assistantService.UpdateAssistantRequest.fromPartial(params), + args, + ); + return Object.assign(p, _connectWithSdk(p)); + }; + + const assistantSdk = { create, get, list, delete: deleteMethod, listVersions, update }; + return assistantSdk; +}; + +type AssistantSdk = ReturnType; + +type GetRunProps = TypeFromProtoc; + +type CreateRunProps = TypeFromProtoc; + +type GetLastRunByThreadProps = TypeFromProtoc; + +type ListRunsProps = TypeFromProtoc; + +type ListenRunProps = TypeFromProtoc; + +export const runWithSdk = >( + session: SessionArg, + run: T, + { + runSdk = initRunSdk(session), + }: { + runSdk?: RunSdk; + }, +) => { + const listen = (params: Omit, args?: ClientCallArgs & CallOptions) => { + return runSdk.listen({ ...params, runId: run.id }, args); + }; + + return Object.assign(run, { listen }); +}; + +export const initRunSdk = (session: SessionArg) => { + const runClient = session.client(runService.RunServiceClient); + + const _connectWithSdk = (runP: Promise) => { + const externalSession = session; + + const withSdk = async (session?: SessionArg | null) => { + const run = await runP; + return runWithSdk(session || externalSession, run, { + runSdk, + }); + }; + + return { withSdk }; + }; + + const create = (params: CreateRunProps, args?: ClientCallArgs) => { + const p = runClient.create(runService.CreateRunRequest.fromPartial(params), args); + return Object.assign(p, _connectWithSdk(p)); + }; + + const get = (params: GetRunProps, args?: ClientCallArgs) => { + const p = runClient.get(runService.GetRunRequest.fromPartial(params), args); + return Object.assign(p, _connectWithSdk(p)); + }; + + const getLastByThread = (params: GetLastRunByThreadProps, args?: ClientCallArgs) => { + const p = runClient.getLastByThread( + runService.GetLastRunByThreadRequest.fromPartial(params), + args, + ); + return Object.assign(p, _connectWithSdk(p)); + }; + + const list = (params: ListRunsProps, args?: ClientCallArgs) => { + const p = runClient.list(runService.ListRunsRequest.fromPartial(params), args); + return p; + }; + + const listen = (params: ListenRunProps, args?: ClientCallArgs & CallOptions) => { + return runClient.listen(runService.ListenRunRequest.fromPartial(params), args); + }; + + const runSdk = { create, get, getLastByThread, list, listen }; + return runSdk; +}; + +type RunSdk = ReturnType; + +export const sdk = (session: SessionArg) => { + const run = initRunSdk(session); + const assistant = initAssistantSdk(session); + const message = initMessageSdk(session); + const thread = initThreadSdk(session); + + return { + run, + assistant, + message, + thread, + }; +}; diff --git a/clients/ai-assistants-v1/types.ts b/clients/ai-assistants-v1/types.ts new file mode 100644 index 00000000..77497f74 --- /dev/null +++ b/clients/ai-assistants-v1/types.ts @@ -0,0 +1,123 @@ +import { ChannelCredentials, ChannelOptions, Client, ServiceDefinition } from '@grpc/grpc-js'; +import { ClientError, RawClient } from 'nice-grpc'; +import { DeadlineOptions } from 'nice-grpc-client-middleware-deadline'; +import { NormalizedServiceDefinition } from 'nice-grpc/lib/service-definitions'; +import { Status } from './generated/google/rpc/status'; + +type RetryOptions = { + /** + * Boolean indicating whether retries are enabled. + * + * If the method is marked as idempotent in Protobuf, i.e. has + * + * option idempotency_level = IDEMPOTENT; + * + * then the default is `true`. Otherwise, the default is `false`. + * + * Method options currently work only when compiling with `ts-proto`. + */ + retry?: boolean; + /** + * Base delay between retry attempts in milliseconds. + * + * Defaults to 1000. + * + * Example: if `retryBaseDelayMs` is 100, then retries will be attempted in + * 100ms, 200ms, 400ms etc. (not counting jitter). + */ + retryBaseDelayMs?: number; + /** + * Maximum delay between attempts in milliseconds. + * + * Defaults to 15 seconds. + * + * Example: if `retryBaseDelayMs` is 1000 and `retryMaxDelayMs` is 3000, then + * retries will be attempted in 1000ms, 2000ms, 3000ms, 3000ms etc (not + * counting jitter). + */ + retryMaxDelayMs?: number; + /** + * Maximum for the total number of attempts. `Infinity` is supported. + * + * Defaults to 1, i.e. a single retry will be attempted. + */ + retryMaxAttempts?: number; + /** + * Array of retryable status codes. + * + * Default is `[UNKNOWN, RESOURCE_EXHAUSTED, INTERNAL, UNAVAILABLE]`. + */ + retryableStatuses?: Status[]; + /** + * Called after receiving error with retryable status code before setting + * backoff delay timer. + * + * If the error code is not retryable, or the maximum attempts exceeded, this + * function will not be called and the error will be thrown from the client + * method. + */ + onRetryableError?(error: ClientError, attempt: number, delayMs: number): void; +}; + +export interface TokenService { + getToken: () => Promise; +} + +export interface GeneratedServiceClientCtor { + service: T; + + new ( + address: string, + credentials: ChannelCredentials, + options?: Partial, + ): Client; +} + +export interface IIAmCredentials { + serviceAccountId: string; + accessKeyId: string; + privateKey: Buffer | string; +} + +export interface ISslCredentials { + rootCertificates?: Buffer; + clientPrivateKey?: Buffer; + clientCertChain?: Buffer; +} + +export interface ChannelSslOptions { + rootCerts?: Buffer; + privateKey?: Buffer; + certChain?: Buffer; +} + +export interface GenericCredentialsConfig { + pollInterval?: number; + ssl?: ChannelSslOptions; + headers?: Record; +} + +export interface OAuthCredentialsConfig extends GenericCredentialsConfig { + oauthToken: string; +} + +export interface IamTokenCredentialsConfig extends GenericCredentialsConfig { + iamToken: string; +} + +export interface ServiceAccountCredentialsConfig extends GenericCredentialsConfig { + serviceAccountJson: IIAmCredentials; +} + +export type SessionConfig = + | OAuthCredentialsConfig + | IamTokenCredentialsConfig + | ServiceAccountCredentialsConfig + | GenericCredentialsConfig; + +export type ClientCallArgs = DeadlineOptions & RetryOptions; + +export type WrappedServiceClientType = RawClient< + NormalizedServiceDefinition, + ClientCallArgs +>; diff --git a/eslint.config.mjs b/eslint.config.mjs index fb0a4a69..2c321483 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -8,4 +8,5 @@ export default [ pluginJs.configs.recommended, { ignores: ['**/generated/**', 'node_modules/**', 'examples/**'] }, ...tseslint.configs.recommended, + ]; diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index 09987c4a..5fa60466 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -4,6 +4,7 @@ "./src", "./config", "./scripts", - "./examples" + "./examples", + "./clients" ] }