From ba86bb25d84721ab911a65193a125bd423d4a8a4 Mon Sep 17 00:00:00 2001 From: snus-kin Date: Wed, 10 Sep 2025 15:59:05 +0100 Subject: [PATCH 1/8] fix: fix generated code and update client and models --- .prettierignore | 1 + .prettierrc | 2 +- athena-protobufs | 2 +- eslint.config.mjs | 1 + package.json | 2 +- src/athena/athena.grpc-client.ts | 159 -- src/athena/athena.ts | 1302 ----------------- src/generated/athena/athena.grpc-client.ts | 120 ++ src/generated/athena/athena.ts | 18 + src/generated/athena/models.ts | 952 ++++++++++++ .../google/protobuf/empty.ts | 101 +- src/hashing.ts | 6 +- src/index.ts | 11 +- 13 files changed, 1146 insertions(+), 1531 deletions(-) create mode 100644 .prettierignore delete mode 100644 src/athena/athena.grpc-client.ts delete mode 100644 src/athena/athena.ts create mode 100644 src/generated/athena/athena.grpc-client.ts create mode 100644 src/generated/athena/athena.ts create mode 100644 src/generated/athena/models.ts rename src/{athena => generated}/google/protobuf/empty.ts (56%) diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..d3db8f9 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +src/generated/ diff --git a/.prettierrc b/.prettierrc index 6858002..04f1f68 100644 --- a/.prettierrc +++ b/.prettierrc @@ -2,5 +2,5 @@ "parser": "typescript", "semi": true, "singleQuote": true, - "trailingComma": "all" + "trailingComma": "all", } diff --git a/athena-protobufs b/athena-protobufs index ce69291..7f69cdf 160000 --- a/athena-protobufs +++ b/athena-protobufs @@ -1 +1 @@ -Subproject commit ce69291a44e8fb2551ab1cc49d805448e57061cd +Subproject commit 7f69cdfb55bc761e18a5e244da034ec361f265dd diff --git a/eslint.config.mjs b/eslint.config.mjs index 8293cbd..c2250b3 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -19,6 +19,7 @@ export default tseslint.config( 'src/athena/**', 'athena-protobufs/**', 'docs/**', + '**/generated/**', ], }, eslint.configs.recommended, diff --git a/package.json b/package.json index 155c4fa..faccc01 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "build": "tsc -p tsconfig.json", "build:watch": "tsc -w -p tsconfig.json", "build:release": "npm run clean && tsc -p tsconfig.release.json", - "codegen": "npx protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --ts_out=import_style=module,client_grpc1:./src/athena --proto_path=./athena-protobufs/athena ./athena-protobufs/athena/athena.proto", + "codegen": "npx protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --ts_out=client_grpc1:./src/generated --proto_path=./athena-protobufs ./athena-protobufs/athena/**.proto", "lint": "eslint .", "lint:all": "npm run lint && npm run prettier:check && npx tsc --noEmit", "precommit:setup": "echo 'To install pre-commit hooks, run: uv tool install pre-commit && uvx pre-commit install'", diff --git a/src/athena/athena.grpc-client.ts b/src/athena/athena.grpc-client.ts deleted file mode 100644 index dc0d819..0000000 --- a/src/athena/athena.grpc-client.ts +++ /dev/null @@ -1,159 +0,0 @@ -// @generated by protobuf-ts 2.11.1 with parameter client_grpc1 -// @generated from protobuf file "athena.proto" (package "athena", syntax proto3) -// tslint:disable -import { ClassifierService } from './athena'; -import type { BinaryWriteOptions } from '@protobuf-ts/runtime'; -import type { BinaryReadOptions } from '@protobuf-ts/runtime'; -import type { ListDeploymentsResponse } from './athena'; -import type { Empty } from './google/protobuf/empty'; -import type { ClassifyResponse } from './athena'; -import type { ClassifyRequest } from './athena'; -import * as grpc from '@grpc/grpc-js'; -/** - * The classifier service definition. - * Provides image classification capabilities with session-based streaming - * and client management functionality. - * - * @generated from protobuf service athena.ClassifierService - */ -export interface IClassifierServiceClient { - /** - * Classify images in a deployment-based streaming context - * Multiple affiliates can join the same deployment to share responses - * Supports bidirectional streaming for real-time classification - * - * @generated from protobuf rpc: Classify - */ - classify( - metadata: grpc.Metadata, - options?: grpc.CallOptions, - ): grpc.ClientDuplexStream; - classify( - options?: grpc.CallOptions, - ): grpc.ClientDuplexStream; - /** - * Retrieves a list of all active deployment IDs - * Returns the active deployment_id values that can be used in Classify requests - * Useful for monitoring and debugging active connections - * - * @generated from protobuf rpc: ListDeployments - */ - listDeployments( - input: Empty, - metadata: grpc.Metadata, - options: grpc.CallOptions, - callback: ( - err: grpc.ServiceError | null, - value?: ListDeploymentsResponse, - ) => void, - ): grpc.ClientUnaryCall; - listDeployments( - input: Empty, - metadata: grpc.Metadata, - callback: ( - err: grpc.ServiceError | null, - value?: ListDeploymentsResponse, - ) => void, - ): grpc.ClientUnaryCall; - listDeployments( - input: Empty, - options: grpc.CallOptions, - callback: ( - err: grpc.ServiceError | null, - value?: ListDeploymentsResponse, - ) => void, - ): grpc.ClientUnaryCall; - listDeployments( - input: Empty, - callback: ( - err: grpc.ServiceError | null, - value?: ListDeploymentsResponse, - ) => void, - ): grpc.ClientUnaryCall; -} -/** - * The classifier service definition. - * Provides image classification capabilities with session-based streaming - * and client management functionality. - * - * @generated from protobuf service athena.ClassifierService - */ -export class ClassifierServiceClient - extends grpc.Client - implements IClassifierServiceClient -{ - private readonly _binaryOptions: Partial< - BinaryReadOptions & BinaryWriteOptions - >; - constructor( - address: string, - credentials: grpc.ChannelCredentials, - options: grpc.ClientOptions = {}, - binaryOptions: Partial = {}, - ) { - super(address, credentials, options); - this._binaryOptions = binaryOptions; - } - /** - * Classify images in a deployment-based streaming context - * Multiple affiliates can join the same deployment to share responses - * Supports bidirectional streaming for real-time classification - * - * @generated from protobuf rpc: Classify - */ - classify( - metadata?: grpc.Metadata | grpc.CallOptions, - options?: grpc.CallOptions, - ): grpc.ClientDuplexStream { - const method = ClassifierService.methods[0]; - return this.makeBidiStreamRequest( - `/${ClassifierService.typeName}/${method.name}`, - (value: ClassifyRequest): Buffer => - Buffer.from(method.I.toBinary(value, this._binaryOptions)), - (value: Buffer): ClassifyResponse => - method.O.fromBinary(value, this._binaryOptions), - metadata as any, - options, - ); - } - /** - * Retrieves a list of all active deployment IDs - * Returns the active deployment_id values that can be used in Classify requests - * Useful for monitoring and debugging active connections - * - * @generated from protobuf rpc: ListDeployments - */ - listDeployments( - input: Empty, - metadata: - | grpc.Metadata - | grpc.CallOptions - | (( - err: grpc.ServiceError | null, - value?: ListDeploymentsResponse, - ) => void), - options?: - | grpc.CallOptions - | (( - err: grpc.ServiceError | null, - value?: ListDeploymentsResponse, - ) => void), - callback?: ( - err: grpc.ServiceError | null, - value?: ListDeploymentsResponse, - ) => void, - ): grpc.ClientUnaryCall { - const method = ClassifierService.methods[1]; - return this.makeUnaryRequest( - `/${ClassifierService.typeName}/${method.name}`, - (value: Empty): Buffer => - Buffer.from(method.I.toBinary(value, this._binaryOptions)), - (value: Buffer): ListDeploymentsResponse => - method.O.fromBinary(value, this._binaryOptions), - input, - metadata as any, - options as any, - callback as any, - ); - } -} diff --git a/src/athena/athena.ts b/src/athena/athena.ts deleted file mode 100644 index 4ae5440..0000000 --- a/src/athena/athena.ts +++ /dev/null @@ -1,1302 +0,0 @@ -// @generated by protobuf-ts 2.11.1 with parameter client_grpc1 -// @generated from protobuf file "athena.proto" (package "athena", syntax proto3) -// tslint:disable -import { Empty } from './google/protobuf/empty'; -import { ServiceType } from '@protobuf-ts/runtime-rpc'; -import type { BinaryWriteOptions } from '@protobuf-ts/runtime'; -import type { IBinaryWriter } from '@protobuf-ts/runtime'; -import { WireType } from '@protobuf-ts/runtime'; -import type { BinaryReadOptions } from '@protobuf-ts/runtime'; -import type { IBinaryReader } from '@protobuf-ts/runtime'; -import { UnknownFieldHandler } from '@protobuf-ts/runtime'; -import type { PartialMessage } from '@protobuf-ts/runtime'; -import { reflectionMergePartial } from '@protobuf-ts/runtime'; -import { MessageType } from '@protobuf-ts/runtime'; -/** - * Response message for ListDeployments RPC - * Contains the list of active deployments and their details. - * - * @generated from protobuf message athena.ListDeploymentsResponse - */ -export interface ListDeploymentsResponse { - /** - * List of active deployments with their backlog information. - * - * @generated from protobuf field: repeated athena.Deployment deployments = 1 - */ - deployments: Deployment[]; -} -/** - * A single active deployment part of a `ListDeployments` response - * - * @generated from protobuf message athena.Deployment - */ -export interface Deployment { - /** - * active deployment identifier - * - * @generated from protobuf field: string deployment_id = 1 - */ - deploymentId: string; - /** - * Backlog of classification responses in this deployment - * - * @generated from protobuf field: int32 backlog = 2 - */ - backlog: number; -} -/** - * The request message containing the image data to classify. - * Each request represents a batch of images that should be processed within - * the same deployment context. - * - * @generated from protobuf message athena.ClassifyRequest - */ -export interface ClassifyRequest { - /** - * Client's unique identifier for this deployment. Responses returned will be - * sent to a client with a matching deployment_id. - * - * @generated from protobuf field: string deployment_id = 1 - */ - deploymentId: string; - /** - * Array of images to be classified in this request batch - * Allows sending multiple images in a single request for efficiency. - * - * @generated from protobuf field: repeated athena.ClassificationInput inputs = 2 - */ - inputs: ClassificationInput[]; -} -/** - * A single image within a classification request batch. - * Contains all necessary metadata and data for classifying one image. - * - * @generated from protobuf message athena.ClassificationInput - */ -export interface ClassificationInput { - /** - * The affiliate or source system that provided this image - * Used for tracking, analytics, and routing purposes. - * - * @generated from protobuf field: string affiliate = 1 - */ - affiliate: string; - /** - * Unique identifier for correlating this input with its response - * Must be unique within the deployment to properly match responses - * - * @generated from protobuf field: string correlation_id = 2 - */ - correlationId: string; - /** - * Specifies the encoding/compression format of the image data - * Allows the server to properly decode the image before classification - * - * @generated from protobuf field: athena.RequestEncoding encoding = 3 - */ - encoding: RequestEncoding; - /** - * The raw image data bytes in the format specified by encoding - * Can be compressed or uncompressed based on the encoding field - * - * @generated from protobuf field: bytes data = 4 - */ - data: Uint8Array; - /** - * The image file format of the data bytes - * - * @generated from protobuf field: athena.ImageFormat format = 5 - */ - format: ImageFormat; - /** - * Hashes of the image data, can be multiple depending on image processing - * (e.g. of raw bytes, jpeg image, etc.) but must be for a single image. - * - * @generated from protobuf field: repeated athena.ImageHash hashes = 6 - */ - hashes: ImageHash[]; -} -/** - * The response message containing the classification results. - * Sent back to clients for each processed batch, containing either - * a global error or individual results for each image in the batch. - * - * @generated from protobuf message athena.ClassifyResponse - */ -export interface ClassifyResponse { - /** - * Global error affecting the entire batch/request - * If present, indicates that the entire request failed and no individual - * image results will be provided - * - * @generated from protobuf field: athena.ClassificationError global_error = 1 - */ - globalError?: ClassificationError; - /** - * Array of classification results, one for each input image - * Will be empty if global_error is present - * - * @generated from protobuf field: repeated athena.ClassificationOutput outputs = 2 - */ - outputs: ClassificationOutput[]; -} -/** - * Individual classification result for a single image. - * Contains the correlation ID and classification results for one image. - * - * @generated from protobuf message athena.ClassificationOutput - */ -export interface ClassificationOutput { - /** - * Matches the correlationId from the corresponding ClassificationInput - * Allows clients to match responses with their original requests - * - * @generated from protobuf field: string correlation_id = 1 - */ - correlationId: string; - /** - * Array of all classifications detected for this image - * Multiple classifications may be returned with different confidence levels - * - * @generated from protobuf field: repeated athena.Classification classifications = 2 - */ - classifications: Classification[]; - /** - * Error information if this specific image classification failed - * If present, indicates that this particular image could not be processed - * - * @generated from protobuf field: athena.ClassificationError error = 3 - */ - error?: ClassificationError; -} -/** - * A single classification result for an image. - * Represents one detected category with its confidence score. - * - * @generated from protobuf message athena.Classification - */ -export interface Classification { - /** - * Human-readable label describing what was classified - * Examples: "CatA", "CatB", "Indicitive", "Distraction", etc. - * - * @generated from protobuf field: string label = 1 - */ - label: string; - /** - * Confidence score between 0.0 and 1.0 indicating certainty - * Higher values indicate greater confidence in the classification - * - * @generated from protobuf field: float weight = 2 - */ - weight: number; -} -/** - * Error information for failed classification attempts. - * Provides details about why a classification could not be completed. - * - * @generated from protobuf message athena.ClassificationError - */ -export interface ClassificationError { - /** - * Error code indicating the type of failure - * - * @generated from protobuf field: athena.ErrorCode code = 1 - */ - code: ErrorCode; - /** - * Human-readable error message providing details about the failure - * - * @generated from protobuf field: string message = 2 - */ - message: string; - /** - * Additional context or details about the error (optional) - * - * @generated from protobuf field: string details = 3 - */ - details: string; -} -/** - * @generated from protobuf message athena.ImageHash - */ -export interface ImageHash { - /** - * The hash value of the image data - * - * @generated from protobuf field: string value = 1 - */ - value: string; - /** - * The type of hash algorithm used to generate the hash value - * - * @generated from protobuf field: athena.HashType type = 2 - */ - type: HashType; -} -/** - * Enumeration of possible classification error codes. - * - * @generated from protobuf enum athena.ErrorCode - */ -export enum ErrorCode { - /** - * Unknown or unspecified error - * - * @generated from protobuf enum value: ERROR_CODE_UNSPECIFIED = 0; - */ - UNSPECIFIED = 0, - /** - * Image is too large to process - * - * @generated from protobuf enum value: ERROR_CODE_IMAGE_TOO_LARGE = 2; - */ - IMAGE_TOO_LARGE = 2, - /** - * Opaque error from the classifier - * - * @generated from protobuf enum value: ERROR_CODE_MODEL_ERROR = 3; - */ - MODEL_ERROR = 3, - /** - * Attempt to send data for an affiliate which is not granted to the current - * client. - * - * @generated from protobuf enum value: ERROR_CODE_AFFILIATE_NOT_PERMITTED = 4; - */ - AFFILIATE_NOT_PERMITTED = 4, - /** - * The format of the deployment ID is not valid. Deployment IDs must be - * non-empty strings of up to 64 characters. - * - * @generated from protobuf enum value: ERROR_CODE_DEPLOYMENT_ID_INVALID = 5; - */ - DEPLOYMENT_ID_INVALID = 5, - /** - * The format of the correlation ID is not valid. Correlation IDs must be - * non-empty strings of up to 64 characters. - * - * @generated from protobuf enum value: ERROR_CODE_CORRELATION_ID_INVALID = 6; - */ - CORRELATION_ID_INVALID = 6, -} -/** - * Enumeration of supported image data encoding formats. - * Determines how the server should interpret the image data bytes. - * - * @generated from protobuf enum athena.RequestEncoding - */ -export enum RequestEncoding { - /** - * Unspecified encoding. Assumed to be uncompressed. - * - * @generated from protobuf enum value: REQUEST_ENCODING_UNSPECIFIED = 0; - */ - UNSPECIFIED = 0, - /** - * Uncompressed raw image data (e.g., raw RGB pixels, BMP, etc.) - * - * @generated from protobuf enum value: REQUEST_ENCODING_UNCOMPRESSED = 1; - */ - UNCOMPRESSED = 1, - /** - * Brotli-compressed image data for reduced bandwidth usage - * Server will decompress using Brotli algorithm before processing - * - * @generated from protobuf enum value: REQUEST_ENCODING_BROTLI = 2; - */ - BROTLI = 2, -} -/** - * Enumeration of supported image file formats. - * Specifies the image file format structure for proper parsing. - * - * @generated from protobuf enum athena.ImageFormat - */ -export enum ImageFormat { - /** - * @generated from protobuf enum value: IMAGE_FORMAT_UNSPECIFIED = 0; - */ - UNSPECIFIED = 0, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_GIF = 1; - */ - GIF = 1, - /** - * Covers .jpeg, .jpg, .jpe extensions - * - * @generated from protobuf enum value: IMAGE_FORMAT_JPEG = 2; - */ - JPEG = 2, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_BMP = 3; - */ - BMP = 3, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_DIB = 4; - */ - DIB = 4, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_PNG = 5; - */ - PNG = 5, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_WEBP = 6; - */ - WEBP = 6, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_PBM = 7; - */ - PBM = 7, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_PGM = 8; - */ - PGM = 8, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_PPM = 9; - */ - PPM = 9, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_PXM = 10; - */ - PXM = 10, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_PNM = 11; - */ - PNM = 11, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_PFM = 12; - */ - PFM = 12, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_SR = 13; - */ - SR = 13, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_RAS = 14; - */ - RAS = 14, - /** - * Covers .tiff, .tif extensions - * - * @generated from protobuf enum value: IMAGE_FORMAT_TIFF = 15; - */ - TIFF = 15, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_HDR = 16; - */ - HDR = 16, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_PIC = 17; - */ - PIC = 17, - /** - * Raw unsigned 8-bit image data (RGB, C order array) - * - * @generated from protobuf enum value: IMAGE_FORMAT_RAW_UINT8 = 18; - */ - RAW_UINT8 = 18, -} -/** - * @generated from protobuf enum athena.HashType - */ -export enum HashType { - /** - * @generated from protobuf enum value: HASH_TYPE_UNKNOWN = 0; - */ - UNKNOWN = 0, - /** - * @generated from protobuf enum value: HASH_TYPE_MD5 = 1; - */ - MD5 = 1, - /** - * @generated from protobuf enum value: HASH_TYPE_SHA1 = 2; - */ - SHA1 = 2, -} -// @generated message type with reflection information, may provide speed optimized methods -class ListDeploymentsResponse$Type extends MessageType { - constructor() { - super('athena.ListDeploymentsResponse', [ - { - no: 1, - name: 'deployments', - kind: 'message', - repeat: 2 /*RepeatType.UNPACKED*/, - T: () => Deployment, - }, - ]); - } - create( - value?: PartialMessage, - ): ListDeploymentsResponse { - const message = globalThis.Object.create(this.messagePrototype!); - message.deployments = []; - if (value !== undefined) - reflectionMergePartial(this, message, value); - return message; - } - internalBinaryRead( - reader: IBinaryReader, - length: number, - options: BinaryReadOptions, - target?: ListDeploymentsResponse, - ): ListDeploymentsResponse { - let message = target ?? this.create(), - end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - case /* repeated athena.Deployment deployments */ 1: - message.deployments.push( - Deployment.internalBinaryRead(reader, reader.uint32(), options), - ); - break; - default: - let u = options.readUnknownField; - if (u === 'throw') - throw new globalThis.Error( - `Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`, - ); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)( - this.typeName, - message, - fieldNo, - wireType, - d, - ); - } - } - return message; - } - internalBinaryWrite( - message: ListDeploymentsResponse, - writer: IBinaryWriter, - options: BinaryWriteOptions, - ): IBinaryWriter { - /* repeated athena.Deployment deployments = 1; */ - for (let i = 0; i < message.deployments.length; i++) - Deployment.internalBinaryWrite( - message.deployments[i], - writer.tag(1, WireType.LengthDelimited).fork(), - options, - ).join(); - let u = options.writeUnknownFields; - if (u !== false) - (u == true ? UnknownFieldHandler.onWrite : u)( - this.typeName, - message, - writer, - ); - return writer; - } -} -/** - * @generated MessageType for protobuf message athena.ListDeploymentsResponse - */ -export const ListDeploymentsResponse = new ListDeploymentsResponse$Type(); -// @generated message type with reflection information, may provide speed optimized methods -class Deployment$Type extends MessageType { - constructor() { - super('athena.Deployment', [ - { - no: 1, - name: 'deployment_id', - kind: 'scalar', - T: 9 /*ScalarType.STRING*/, - }, - { no: 2, name: 'backlog', kind: 'scalar', T: 5 /*ScalarType.INT32*/ }, - ]); - } - create(value?: PartialMessage): Deployment { - const message = globalThis.Object.create(this.messagePrototype!); - message.deploymentId = ''; - message.backlog = 0; - if (value !== undefined) - reflectionMergePartial(this, message, value); - return message; - } - internalBinaryRead( - reader: IBinaryReader, - length: number, - options: BinaryReadOptions, - target?: Deployment, - ): Deployment { - let message = target ?? this.create(), - end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - case /* string deployment_id */ 1: - message.deploymentId = reader.string(); - break; - case /* int32 backlog */ 2: - message.backlog = reader.int32(); - break; - default: - let u = options.readUnknownField; - if (u === 'throw') - throw new globalThis.Error( - `Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`, - ); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)( - this.typeName, - message, - fieldNo, - wireType, - d, - ); - } - } - return message; - } - internalBinaryWrite( - message: Deployment, - writer: IBinaryWriter, - options: BinaryWriteOptions, - ): IBinaryWriter { - /* string deployment_id = 1; */ - if (message.deploymentId !== '') - writer.tag(1, WireType.LengthDelimited).string(message.deploymentId); - /* int32 backlog = 2; */ - if (message.backlog !== 0) - writer.tag(2, WireType.Varint).int32(message.backlog); - let u = options.writeUnknownFields; - if (u !== false) - (u == true ? UnknownFieldHandler.onWrite : u)( - this.typeName, - message, - writer, - ); - return writer; - } -} -/** - * @generated MessageType for protobuf message athena.Deployment - */ -export const Deployment = new Deployment$Type(); -// @generated message type with reflection information, may provide speed optimized methods -class ClassifyRequest$Type extends MessageType { - constructor() { - super('athena.ClassifyRequest', [ - { - no: 1, - name: 'deployment_id', - kind: 'scalar', - T: 9 /*ScalarType.STRING*/, - }, - { - no: 2, - name: 'inputs', - kind: 'message', - repeat: 2 /*RepeatType.UNPACKED*/, - T: () => ClassificationInput, - }, - ]); - } - create(value?: PartialMessage): ClassifyRequest { - const message = globalThis.Object.create(this.messagePrototype!); - message.deploymentId = ''; - message.inputs = []; - if (value !== undefined) - reflectionMergePartial(this, message, value); - return message; - } - internalBinaryRead( - reader: IBinaryReader, - length: number, - options: BinaryReadOptions, - target?: ClassifyRequest, - ): ClassifyRequest { - let message = target ?? this.create(), - end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - case /* string deployment_id */ 1: - message.deploymentId = reader.string(); - break; - case /* repeated athena.ClassificationInput inputs */ 2: - message.inputs.push( - ClassificationInput.internalBinaryRead( - reader, - reader.uint32(), - options, - ), - ); - break; - default: - let u = options.readUnknownField; - if (u === 'throw') - throw new globalThis.Error( - `Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`, - ); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)( - this.typeName, - message, - fieldNo, - wireType, - d, - ); - } - } - return message; - } - internalBinaryWrite( - message: ClassifyRequest, - writer: IBinaryWriter, - options: BinaryWriteOptions, - ): IBinaryWriter { - /* string deployment_id = 1; */ - if (message.deploymentId !== '') - writer.tag(1, WireType.LengthDelimited).string(message.deploymentId); - /* repeated athena.ClassificationInput inputs = 2; */ - for (let i = 0; i < message.inputs.length; i++) - ClassificationInput.internalBinaryWrite( - message.inputs[i], - writer.tag(2, WireType.LengthDelimited).fork(), - options, - ).join(); - let u = options.writeUnknownFields; - if (u !== false) - (u == true ? UnknownFieldHandler.onWrite : u)( - this.typeName, - message, - writer, - ); - return writer; - } -} -/** - * @generated MessageType for protobuf message athena.ClassifyRequest - */ -export const ClassifyRequest = new ClassifyRequest$Type(); -// @generated message type with reflection information, may provide speed optimized methods -class ClassificationInput$Type extends MessageType { - constructor() { - super('athena.ClassificationInput', [ - { no: 1, name: 'affiliate', kind: 'scalar', T: 9 /*ScalarType.STRING*/ }, - { - no: 2, - name: 'correlation_id', - kind: 'scalar', - T: 9 /*ScalarType.STRING*/, - }, - { - no: 3, - name: 'encoding', - kind: 'enum', - T: () => [ - 'athena.RequestEncoding', - RequestEncoding, - 'REQUEST_ENCODING_', - ], - }, - { no: 4, name: 'data', kind: 'scalar', T: 12 /*ScalarType.BYTES*/ }, - { - no: 5, - name: 'format', - kind: 'enum', - T: () => ['athena.ImageFormat', ImageFormat, 'IMAGE_FORMAT_'], - }, - { - no: 6, - name: 'hashes', - kind: 'message', - repeat: 2 /*RepeatType.UNPACKED*/, - T: () => ImageHash, - }, - ]); - } - create(value?: PartialMessage): ClassificationInput { - const message = globalThis.Object.create(this.messagePrototype!); - message.affiliate = ''; - message.correlationId = ''; - message.encoding = 0; - message.data = new Uint8Array(0); - message.format = 0; - message.hashes = []; - if (value !== undefined) - reflectionMergePartial(this, message, value); - return message; - } - internalBinaryRead( - reader: IBinaryReader, - length: number, - options: BinaryReadOptions, - target?: ClassificationInput, - ): ClassificationInput { - let message = target ?? this.create(), - end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - case /* string affiliate */ 1: - message.affiliate = reader.string(); - break; - case /* string correlation_id */ 2: - message.correlationId = reader.string(); - break; - case /* athena.RequestEncoding encoding */ 3: - message.encoding = reader.int32(); - break; - case /* bytes data */ 4: - message.data = reader.bytes(); - break; - case /* athena.ImageFormat format */ 5: - message.format = reader.int32(); - break; - case /* repeated athena.ImageHash hashes */ 6: - message.hashes.push( - ImageHash.internalBinaryRead(reader, reader.uint32(), options), - ); - break; - default: - let u = options.readUnknownField; - if (u === 'throw') - throw new globalThis.Error( - `Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`, - ); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)( - this.typeName, - message, - fieldNo, - wireType, - d, - ); - } - } - return message; - } - internalBinaryWrite( - message: ClassificationInput, - writer: IBinaryWriter, - options: BinaryWriteOptions, - ): IBinaryWriter { - /* string affiliate = 1; */ - if (message.affiliate !== '') - writer.tag(1, WireType.LengthDelimited).string(message.affiliate); - /* string correlation_id = 2; */ - if (message.correlationId !== '') - writer.tag(2, WireType.LengthDelimited).string(message.correlationId); - /* athena.RequestEncoding encoding = 3; */ - if (message.encoding !== 0) - writer.tag(3, WireType.Varint).int32(message.encoding); - /* bytes data = 4; */ - if (message.data.length) - writer.tag(4, WireType.LengthDelimited).bytes(message.data); - /* athena.ImageFormat format = 5; */ - if (message.format !== 0) - writer.tag(5, WireType.Varint).int32(message.format); - /* repeated athena.ImageHash hashes = 6; */ - for (let i = 0; i < message.hashes.length; i++) - ImageHash.internalBinaryWrite( - message.hashes[i], - writer.tag(6, WireType.LengthDelimited).fork(), - options, - ).join(); - let u = options.writeUnknownFields; - if (u !== false) - (u == true ? UnknownFieldHandler.onWrite : u)( - this.typeName, - message, - writer, - ); - return writer; - } -} -/** - * @generated MessageType for protobuf message athena.ClassificationInput - */ -export const ClassificationInput = new ClassificationInput$Type(); -// @generated message type with reflection information, may provide speed optimized methods -class ClassifyResponse$Type extends MessageType { - constructor() { - super('athena.ClassifyResponse', [ - { - no: 1, - name: 'global_error', - kind: 'message', - T: () => ClassificationError, - }, - { - no: 2, - name: 'outputs', - kind: 'message', - repeat: 2 /*RepeatType.UNPACKED*/, - T: () => ClassificationOutput, - }, - ]); - } - create(value?: PartialMessage): ClassifyResponse { - const message = globalThis.Object.create(this.messagePrototype!); - message.outputs = []; - if (value !== undefined) - reflectionMergePartial(this, message, value); - return message; - } - internalBinaryRead( - reader: IBinaryReader, - length: number, - options: BinaryReadOptions, - target?: ClassifyResponse, - ): ClassifyResponse { - let message = target ?? this.create(), - end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - case /* athena.ClassificationError global_error */ 1: - message.globalError = ClassificationError.internalBinaryRead( - reader, - reader.uint32(), - options, - message.globalError, - ); - break; - case /* repeated athena.ClassificationOutput outputs */ 2: - message.outputs.push( - ClassificationOutput.internalBinaryRead( - reader, - reader.uint32(), - options, - ), - ); - break; - default: - let u = options.readUnknownField; - if (u === 'throw') - throw new globalThis.Error( - `Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`, - ); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)( - this.typeName, - message, - fieldNo, - wireType, - d, - ); - } - } - return message; - } - internalBinaryWrite( - message: ClassifyResponse, - writer: IBinaryWriter, - options: BinaryWriteOptions, - ): IBinaryWriter { - /* athena.ClassificationError global_error = 1; */ - if (message.globalError) - ClassificationError.internalBinaryWrite( - message.globalError, - writer.tag(1, WireType.LengthDelimited).fork(), - options, - ).join(); - /* repeated athena.ClassificationOutput outputs = 2; */ - for (let i = 0; i < message.outputs.length; i++) - ClassificationOutput.internalBinaryWrite( - message.outputs[i], - writer.tag(2, WireType.LengthDelimited).fork(), - options, - ).join(); - let u = options.writeUnknownFields; - if (u !== false) - (u == true ? UnknownFieldHandler.onWrite : u)( - this.typeName, - message, - writer, - ); - return writer; - } -} -/** - * @generated MessageType for protobuf message athena.ClassifyResponse - */ -export const ClassifyResponse = new ClassifyResponse$Type(); -// @generated message type with reflection information, may provide speed optimized methods -class ClassificationOutput$Type extends MessageType { - constructor() { - super('athena.ClassificationOutput', [ - { - no: 1, - name: 'correlation_id', - kind: 'scalar', - T: 9 /*ScalarType.STRING*/, - }, - { - no: 2, - name: 'classifications', - kind: 'message', - repeat: 2 /*RepeatType.UNPACKED*/, - T: () => Classification, - }, - { no: 3, name: 'error', kind: 'message', T: () => ClassificationError }, - ]); - } - create(value?: PartialMessage): ClassificationOutput { - const message = globalThis.Object.create(this.messagePrototype!); - message.correlationId = ''; - message.classifications = []; - if (value !== undefined) - reflectionMergePartial(this, message, value); - return message; - } - internalBinaryRead( - reader: IBinaryReader, - length: number, - options: BinaryReadOptions, - target?: ClassificationOutput, - ): ClassificationOutput { - let message = target ?? this.create(), - end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - case /* string correlation_id */ 1: - message.correlationId = reader.string(); - break; - case /* repeated athena.Classification classifications */ 2: - message.classifications.push( - Classification.internalBinaryRead(reader, reader.uint32(), options), - ); - break; - case /* athena.ClassificationError error */ 3: - message.error = ClassificationError.internalBinaryRead( - reader, - reader.uint32(), - options, - message.error, - ); - break; - default: - let u = options.readUnknownField; - if (u === 'throw') - throw new globalThis.Error( - `Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`, - ); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)( - this.typeName, - message, - fieldNo, - wireType, - d, - ); - } - } - return message; - } - internalBinaryWrite( - message: ClassificationOutput, - writer: IBinaryWriter, - options: BinaryWriteOptions, - ): IBinaryWriter { - /* string correlation_id = 1; */ - if (message.correlationId !== '') - writer.tag(1, WireType.LengthDelimited).string(message.correlationId); - /* repeated athena.Classification classifications = 2; */ - for (let i = 0; i < message.classifications.length; i++) - Classification.internalBinaryWrite( - message.classifications[i], - writer.tag(2, WireType.LengthDelimited).fork(), - options, - ).join(); - /* athena.ClassificationError error = 3; */ - if (message.error) - ClassificationError.internalBinaryWrite( - message.error, - writer.tag(3, WireType.LengthDelimited).fork(), - options, - ).join(); - let u = options.writeUnknownFields; - if (u !== false) - (u == true ? UnknownFieldHandler.onWrite : u)( - this.typeName, - message, - writer, - ); - return writer; - } -} -/** - * @generated MessageType for protobuf message athena.ClassificationOutput - */ -export const ClassificationOutput = new ClassificationOutput$Type(); -// @generated message type with reflection information, may provide speed optimized methods -class Classification$Type extends MessageType { - constructor() { - super('athena.Classification', [ - { no: 1, name: 'label', kind: 'scalar', T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: 'weight', kind: 'scalar', T: 2 /*ScalarType.FLOAT*/ }, - ]); - } - create(value?: PartialMessage): Classification { - const message = globalThis.Object.create(this.messagePrototype!); - message.label = ''; - message.weight = 0; - if (value !== undefined) - reflectionMergePartial(this, message, value); - return message; - } - internalBinaryRead( - reader: IBinaryReader, - length: number, - options: BinaryReadOptions, - target?: Classification, - ): Classification { - let message = target ?? this.create(), - end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - case /* string label */ 1: - message.label = reader.string(); - break; - case /* float weight */ 2: - message.weight = reader.float(); - break; - default: - let u = options.readUnknownField; - if (u === 'throw') - throw new globalThis.Error( - `Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`, - ); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)( - this.typeName, - message, - fieldNo, - wireType, - d, - ); - } - } - return message; - } - internalBinaryWrite( - message: Classification, - writer: IBinaryWriter, - options: BinaryWriteOptions, - ): IBinaryWriter { - /* string label = 1; */ - if (message.label !== '') - writer.tag(1, WireType.LengthDelimited).string(message.label); - /* float weight = 2; */ - if (message.weight !== 0) - writer.tag(2, WireType.Bit32).float(message.weight); - let u = options.writeUnknownFields; - if (u !== false) - (u == true ? UnknownFieldHandler.onWrite : u)( - this.typeName, - message, - writer, - ); - return writer; - } -} -/** - * @generated MessageType for protobuf message athena.Classification - */ -export const Classification = new Classification$Type(); -// @generated message type with reflection information, may provide speed optimized methods -class ClassificationError$Type extends MessageType { - constructor() { - super('athena.ClassificationError', [ - { - no: 1, - name: 'code', - kind: 'enum', - T: () => ['athena.ErrorCode', ErrorCode, 'ERROR_CODE_'], - }, - { no: 2, name: 'message', kind: 'scalar', T: 9 /*ScalarType.STRING*/ }, - { no: 3, name: 'details', kind: 'scalar', T: 9 /*ScalarType.STRING*/ }, - ]); - } - create(value?: PartialMessage): ClassificationError { - const message = globalThis.Object.create(this.messagePrototype!); - message.code = 0; - message.message = ''; - message.details = ''; - if (value !== undefined) - reflectionMergePartial(this, message, value); - return message; - } - internalBinaryRead( - reader: IBinaryReader, - length: number, - options: BinaryReadOptions, - target?: ClassificationError, - ): ClassificationError { - let message = target ?? this.create(), - end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - case /* athena.ErrorCode code */ 1: - message.code = reader.int32(); - break; - case /* string message */ 2: - message.message = reader.string(); - break; - case /* string details */ 3: - message.details = reader.string(); - break; - default: - let u = options.readUnknownField; - if (u === 'throw') - throw new globalThis.Error( - `Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`, - ); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)( - this.typeName, - message, - fieldNo, - wireType, - d, - ); - } - } - return message; - } - internalBinaryWrite( - message: ClassificationError, - writer: IBinaryWriter, - options: BinaryWriteOptions, - ): IBinaryWriter { - /* athena.ErrorCode code = 1; */ - if (message.code !== 0) writer.tag(1, WireType.Varint).int32(message.code); - /* string message = 2; */ - if (message.message !== '') - writer.tag(2, WireType.LengthDelimited).string(message.message); - /* string details = 3; */ - if (message.details !== '') - writer.tag(3, WireType.LengthDelimited).string(message.details); - let u = options.writeUnknownFields; - if (u !== false) - (u == true ? UnknownFieldHandler.onWrite : u)( - this.typeName, - message, - writer, - ); - return writer; - } -} -/** - * @generated MessageType for protobuf message athena.ClassificationError - */ -export const ClassificationError = new ClassificationError$Type(); -// @generated message type with reflection information, may provide speed optimized methods -class ImageHash$Type extends MessageType { - constructor() { - super('athena.ImageHash', [ - { no: 1, name: 'value', kind: 'scalar', T: 9 /*ScalarType.STRING*/ }, - { - no: 2, - name: 'type', - kind: 'enum', - T: () => ['athena.HashType', HashType, 'HASH_TYPE_'], - }, - ]); - } - create(value?: PartialMessage): ImageHash { - const message = globalThis.Object.create(this.messagePrototype!); - message.value = ''; - message.type = 0; - if (value !== undefined) - reflectionMergePartial(this, message, value); - return message; - } - internalBinaryRead( - reader: IBinaryReader, - length: number, - options: BinaryReadOptions, - target?: ImageHash, - ): ImageHash { - let message = target ?? this.create(), - end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - case /* string value */ 1: - message.value = reader.string(); - break; - case /* athena.HashType type */ 2: - message.type = reader.int32(); - break; - default: - let u = options.readUnknownField; - if (u === 'throw') - throw new globalThis.Error( - `Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`, - ); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)( - this.typeName, - message, - fieldNo, - wireType, - d, - ); - } - } - return message; - } - internalBinaryWrite( - message: ImageHash, - writer: IBinaryWriter, - options: BinaryWriteOptions, - ): IBinaryWriter { - /* string value = 1; */ - if (message.value !== '') - writer.tag(1, WireType.LengthDelimited).string(message.value); - /* athena.HashType type = 2; */ - if (message.type !== 0) writer.tag(2, WireType.Varint).int32(message.type); - let u = options.writeUnknownFields; - if (u !== false) - (u == true ? UnknownFieldHandler.onWrite : u)( - this.typeName, - message, - writer, - ); - return writer; - } -} -/** - * @generated MessageType for protobuf message athena.ImageHash - */ -export const ImageHash = new ImageHash$Type(); -/** - * @generated ServiceType for protobuf service athena.ClassifierService - */ -export const ClassifierService = new ServiceType('athena.ClassifierService', [ - { - name: 'Classify', - serverStreaming: true, - clientStreaming: true, - options: {}, - I: ClassifyRequest, - O: ClassifyResponse, - }, - { - name: 'ListDeployments', - options: {}, - I: Empty, - O: ListDeploymentsResponse, - }, -]); diff --git a/src/generated/athena/athena.grpc-client.ts b/src/generated/athena/athena.grpc-client.ts new file mode 100644 index 0000000..df24522 --- /dev/null +++ b/src/generated/athena/athena.grpc-client.ts @@ -0,0 +1,120 @@ +// @generated by protobuf-ts 2.11.1 with parameter client_grpc1 +// @generated from protobuf file "athena/athena.proto" (package "athena", syntax proto3) +// tslint:disable +import { ClassifierService } from "./athena"; +import type { BinaryWriteOptions } from "@protobuf-ts/runtime"; +import type { BinaryReadOptions } from "@protobuf-ts/runtime"; +import type { ClassificationOutput } from "./models"; +import type { ClassificationInput } from "./models"; +import type { ListDeploymentsResponse } from "./models"; +import type { Empty } from "../google/protobuf/empty"; +import type { ClassifyResponse } from "./models"; +import type { ClassifyRequest } from "./models"; +import * as grpc from "@grpc/grpc-js"; +/** + * The classifier service definition. + * Provides image classification capabilities with session-based streaming + * and client management functionality. + * + * @generated from protobuf service athena.ClassifierService + */ +export interface IClassifierServiceClient { + /** + * Classify images in a deployment-based streaming context + * Multiple affiliates can join the same deployment to share responses + * Supports bidirectional streaming for real-time classification + * + * @generated from protobuf rpc: Classify + */ + classify(metadata: grpc.Metadata, options?: grpc.CallOptions): grpc.ClientDuplexStream; + classify(options?: grpc.CallOptions): grpc.ClientDuplexStream; + /** + * Retrieves a list of all active deployment IDs + * Returns the active deployment_id values that can be used in Classify requests + * Useful for monitoring and debugging active connections + * + * @generated from protobuf rpc: ListDeployments + */ + listDeployments(input: Empty, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: ListDeploymentsResponse) => void): grpc.ClientUnaryCall; + listDeployments(input: Empty, metadata: grpc.Metadata, callback: (err: grpc.ServiceError | null, value?: ListDeploymentsResponse) => void): grpc.ClientUnaryCall; + listDeployments(input: Empty, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: ListDeploymentsResponse) => void): grpc.ClientUnaryCall; + listDeployments(input: Empty, callback: (err: grpc.ServiceError | null, value?: ListDeploymentsResponse) => void): grpc.ClientUnaryCall; + /** + * Classifies a single image synchronously without deployment context + * Returns classification results immediately in a single request-response cycle + * Unlike the streaming Classify method, this operates independently of deployments + * and does not require session management or deployment coordination + * + * Use this for: + * - Low-throughput, low-latency classification scenarios + * - Simple one-off image classifications + * - Applications where immediate synchronous responses are preferred over streaming + * - Testing and debugging individual image classifications + * + * The response will contain either classification results or error information + * No deployment_id coordination is required or supported + * + * @generated from protobuf rpc: ClassifySingle + */ + classifySingle(input: ClassificationInput, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: ClassificationOutput) => void): grpc.ClientUnaryCall; + classifySingle(input: ClassificationInput, metadata: grpc.Metadata, callback: (err: grpc.ServiceError | null, value?: ClassificationOutput) => void): grpc.ClientUnaryCall; + classifySingle(input: ClassificationInput, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: ClassificationOutput) => void): grpc.ClientUnaryCall; + classifySingle(input: ClassificationInput, callback: (err: grpc.ServiceError | null, value?: ClassificationOutput) => void): grpc.ClientUnaryCall; +} +/** + * The classifier service definition. + * Provides image classification capabilities with session-based streaming + * and client management functionality. + * + * @generated from protobuf service athena.ClassifierService + */ +export class ClassifierServiceClient extends grpc.Client implements IClassifierServiceClient { + private readonly _binaryOptions: Partial; + constructor(address: string, credentials: grpc.ChannelCredentials, options: grpc.ClientOptions = {}, binaryOptions: Partial = {}) { + super(address, credentials, options); + this._binaryOptions = binaryOptions; + } + /** + * Classify images in a deployment-based streaming context + * Multiple affiliates can join the same deployment to share responses + * Supports bidirectional streaming for real-time classification + * + * @generated from protobuf rpc: Classify + */ + classify(metadata?: grpc.Metadata | grpc.CallOptions, options?: grpc.CallOptions): grpc.ClientDuplexStream { + const method = ClassifierService.methods[0]; + return this.makeBidiStreamRequest(`/${ClassifierService.typeName}/${method.name}`, (value: ClassifyRequest): Buffer => Buffer.from(method.I.toBinary(value, this._binaryOptions)), (value: Buffer): ClassifyResponse => method.O.fromBinary(value, this._binaryOptions), (metadata as any), options); + } + /** + * Retrieves a list of all active deployment IDs + * Returns the active deployment_id values that can be used in Classify requests + * Useful for monitoring and debugging active connections + * + * @generated from protobuf rpc: ListDeployments + */ + listDeployments(input: Empty, metadata: grpc.Metadata | grpc.CallOptions | ((err: grpc.ServiceError | null, value?: ListDeploymentsResponse) => void), options?: grpc.CallOptions | ((err: grpc.ServiceError | null, value?: ListDeploymentsResponse) => void), callback?: ((err: grpc.ServiceError | null, value?: ListDeploymentsResponse) => void)): grpc.ClientUnaryCall { + const method = ClassifierService.methods[1]; + return this.makeUnaryRequest(`/${ClassifierService.typeName}/${method.name}`, (value: Empty): Buffer => Buffer.from(method.I.toBinary(value, this._binaryOptions)), (value: Buffer): ListDeploymentsResponse => method.O.fromBinary(value, this._binaryOptions), input, (metadata as any), (options as any), (callback as any)); + } + /** + * Classifies a single image synchronously without deployment context + * Returns classification results immediately in a single request-response cycle + * Unlike the streaming Classify method, this operates independently of deployments + * and does not require session management or deployment coordination + * + * Use this for: + * - Low-throughput, low-latency classification scenarios + * - Simple one-off image classifications + * - Applications where immediate synchronous responses are preferred over streaming + * - Testing and debugging individual image classifications + * + * The response will contain either classification results or error information + * No deployment_id coordination is required or supported + * + * @generated from protobuf rpc: ClassifySingle + */ + classifySingle(input: ClassificationInput, metadata: grpc.Metadata | grpc.CallOptions | ((err: grpc.ServiceError | null, value?: ClassificationOutput) => void), options?: grpc.CallOptions | ((err: grpc.ServiceError | null, value?: ClassificationOutput) => void), callback?: ((err: grpc.ServiceError | null, value?: ClassificationOutput) => void)): grpc.ClientUnaryCall { + const method = ClassifierService.methods[2]; + return this.makeUnaryRequest(`/${ClassifierService.typeName}/${method.name}`, (value: ClassificationInput): Buffer => Buffer.from(method.I.toBinary(value, this._binaryOptions)), (value: Buffer): ClassificationOutput => method.O.fromBinary(value, this._binaryOptions), input, (metadata as any), (options as any), (callback as any)); + } +} diff --git a/src/generated/athena/athena.ts b/src/generated/athena/athena.ts new file mode 100644 index 0000000..8624373 --- /dev/null +++ b/src/generated/athena/athena.ts @@ -0,0 +1,18 @@ +// @generated by protobuf-ts 2.11.1 with parameter client_grpc1 +// @generated from protobuf file "athena/athena.proto" (package "athena", syntax proto3) +// tslint:disable +import { ClassificationOutput } from "./models"; +import { ClassificationInput } from "./models"; +import { ListDeploymentsResponse } from "./models"; +import { Empty } from "../google/protobuf/empty"; +import { ClassifyResponse } from "./models"; +import { ClassifyRequest } from "./models"; +import { ServiceType } from "@protobuf-ts/runtime-rpc"; +/** + * @generated ServiceType for protobuf service athena.ClassifierService + */ +export const ClassifierService = new ServiceType("athena.ClassifierService", [ + { name: "Classify", serverStreaming: true, clientStreaming: true, options: {}, I: ClassifyRequest, O: ClassifyResponse }, + { name: "ListDeployments", options: {}, I: Empty, O: ListDeploymentsResponse }, + { name: "ClassifySingle", options: {}, I: ClassificationInput, O: ClassificationOutput } +]); diff --git a/src/generated/athena/models.ts b/src/generated/athena/models.ts new file mode 100644 index 0000000..ae6fc36 --- /dev/null +++ b/src/generated/athena/models.ts @@ -0,0 +1,952 @@ +// @generated by protobuf-ts 2.11.1 with parameter client_grpc1 +// @generated from protobuf file "athena/models.proto" (package "athena", syntax proto3) +// tslint:disable +import type { BinaryWriteOptions } from "@protobuf-ts/runtime"; +import type { IBinaryWriter } from "@protobuf-ts/runtime"; +import { WireType } from "@protobuf-ts/runtime"; +import type { BinaryReadOptions } from "@protobuf-ts/runtime"; +import type { IBinaryReader } from "@protobuf-ts/runtime"; +import { UnknownFieldHandler } from "@protobuf-ts/runtime"; +import type { PartialMessage } from "@protobuf-ts/runtime"; +import { reflectionMergePartial } from "@protobuf-ts/runtime"; +import { MessageType } from "@protobuf-ts/runtime"; +/** + * Response message for ListDeployments RPC + * Contains the list of active deployments and their details. + * + * @generated from protobuf message athena.ListDeploymentsResponse + */ +export interface ListDeploymentsResponse { + /** + * List of active deployments with their backlog information. + * + * @generated from protobuf field: repeated athena.Deployment deployments = 1 + */ + deployments: Deployment[]; +} +/** + * A single active deployment part of a `ListDeployments` response + * + * @generated from protobuf message athena.Deployment + */ +export interface Deployment { + /** + * active deployment identifier + * + * @generated from protobuf field: string deployment_id = 1 + */ + deploymentId: string; + /** + * Backlog of classification responses in this deployment + * + * @generated from protobuf field: int32 backlog = 2 + */ + backlog: number; +} +/** + * The request message containing the image data to classify. + * Each request represents a batch of images that should be processed within + * the same deployment context. + * + * @generated from protobuf message athena.ClassifyRequest + */ +export interface ClassifyRequest { + /** + * Client's unique identifier for this deployment. Responses returned will be + * sent to a client with a matching deployment_id. + * + * @generated from protobuf field: string deployment_id = 1 + */ + deploymentId: string; + /** + * Array of images to be classified in this request batch + * Allows sending multiple images in a single request for efficiency. + * + * @generated from protobuf field: repeated athena.ClassificationInput inputs = 2 + */ + inputs: ClassificationInput[]; +} +/** + * A single image within a classification request batch. + * Contains all necessary metadata and data for classifying one image. + * + * @generated from protobuf message athena.ClassificationInput + */ +export interface ClassificationInput { + /** + * The affiliate or source system that provided this image + * Used for tracking, analytics, and routing purposes. + * + * @generated from protobuf field: string affiliate = 1 + */ + affiliate: string; + /** + * Unique identifier for correlating this input with its response + * Must be unique within the deployment to properly match responses + * + * @generated from protobuf field: string correlation_id = 2 + */ + correlationId: string; + /** + * Specifies the encoding/compression format of the image data + * Allows the server to properly decode the image before classification + * + * @generated from protobuf field: athena.RequestEncoding encoding = 3 + */ + encoding: RequestEncoding; + /** + * The raw image data bytes in the format specified by encoding + * Can be compressed or uncompressed based on the encoding field + * + * @generated from protobuf field: bytes data = 4 + */ + data: Uint8Array; + /** + * The image file format of the data bytes + * + * @generated from protobuf field: athena.ImageFormat format = 5 + */ + format: ImageFormat; + /** + * Hashes of the image data, can be multiple depending on image processing + * (e.g. of raw bytes, jpeg image, etc.) but must be for a single image. + * + * @generated from protobuf field: repeated athena.ImageHash hashes = 6 + */ + hashes: ImageHash[]; +} +/** + * The response message containing the classification results. + * Sent back to clients for each processed batch, containing either + * a global error or individual results for each image in the batch. + * + * @generated from protobuf message athena.ClassifyResponse + */ +export interface ClassifyResponse { + /** + * Global error affecting the entire batch/request + * If present, indicates that the entire request failed and no individual + * image results will be provided + * + * @generated from protobuf field: athena.ClassificationError global_error = 1 + */ + globalError?: ClassificationError; + /** + * Array of classification results, one for each input image + * Will be empty if global_error is present + * + * @generated from protobuf field: repeated athena.ClassificationOutput outputs = 2 + */ + outputs: ClassificationOutput[]; +} +/** + * Individual classification result for a single image. + * Contains the correlation ID and classification results for one image. + * + * @generated from protobuf message athena.ClassificationOutput + */ +export interface ClassificationOutput { + /** + * Matches the correlationId from the corresponding ClassificationInput + * Allows clients to match responses with their original requests + * + * @generated from protobuf field: string correlation_id = 1 + */ + correlationId: string; + /** + * Array of all classifications detected for this image + * Multiple classifications may be returned with different confidence levels + * + * @generated from protobuf field: repeated athena.Classification classifications = 2 + */ + classifications: Classification[]; + /** + * Error information if this specific image classification failed + * If present, indicates that this particular image could not be processed + * + * @generated from protobuf field: athena.ClassificationError error = 3 + */ + error?: ClassificationError; +} +/** + * A single classification result for an image. + * Represents one detected category with its confidence score. + * + * @generated from protobuf message athena.Classification + */ +export interface Classification { + /** + * Human-readable label describing what was classified + * Examples: "CatA", "CatB", "Indicative", "Distraction", etc. + * + * @generated from protobuf field: string label = 1 + */ + label: string; + /** + * Confidence score between 0.0 and 1.0 indicating certainty + * Higher values indicate greater confidence in the classification + * + * @generated from protobuf field: float weight = 2 + */ + weight: number; +} +/** + * Error information for failed classification attempts. + * Provides details about why a classification could not be completed. + * + * @generated from protobuf message athena.ClassificationError + */ +export interface ClassificationError { + /** + * Error code indicating the type of failure + * + * @generated from protobuf field: athena.ErrorCode code = 1 + */ + code: ErrorCode; + /** + * Human-readable error message providing details about the failure + * + * @generated from protobuf field: string message = 2 + */ + message: string; + /** + * Additional context or details about the error (optional) + * + * @generated from protobuf field: string details = 3 + */ + details: string; +} +/** + * Hash information for an image. + * + * @generated from protobuf message athena.ImageHash + */ +export interface ImageHash { + /** + * The hash value of the image data + * + * @generated from protobuf field: string value = 1 + */ + value: string; + /** + * The type of hash algorithm used to generate the hash value + * + * @generated from protobuf field: athena.HashType type = 2 + */ + type: HashType; +} +/** + * Enumeration of possible classification error codes. + * + * @generated from protobuf enum athena.ErrorCode + */ +export enum ErrorCode { + /** + * Unknown or unspecified error + * + * @generated from protobuf enum value: ERROR_CODE_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + /** + * Image is too large to process + * + * @generated from protobuf enum value: ERROR_CODE_IMAGE_TOO_LARGE = 2; + */ + IMAGE_TOO_LARGE = 2, + /** + * Opaque error from the classifier + * + * @generated from protobuf enum value: ERROR_CODE_MODEL_ERROR = 3; + */ + MODEL_ERROR = 3, + /** + * Attempt to send data for an affiliate which is not granted to the current + * client. + * + * @generated from protobuf enum value: ERROR_CODE_AFFILIATE_NOT_PERMITTED = 4; + */ + AFFILIATE_NOT_PERMITTED = 4, + /** + * The format of the deployment ID is not valid. Deployment IDs must be + * non-empty strings of up to 64 characters. + * + * @generated from protobuf enum value: ERROR_CODE_DEPLOYMENT_ID_INVALID = 5; + */ + DEPLOYMENT_ID_INVALID = 5, + /** + * The format of the correlation ID is not valid. Correlation IDs must be + * non-empty strings of up to 64 characters. + * + * @generated from protobuf enum value: ERROR_CODE_CORRELATION_ID_INVALID = 6; + */ + CORRELATION_ID_INVALID = 6 +} +/** + * Enumeration of supported image data encoding formats. + * Determines how the server should interpret the image data bytes. + * + * @generated from protobuf enum athena.RequestEncoding + */ +export enum RequestEncoding { + /** + * Unspecified encoding. Assumed to be uncompressed. + * + * @generated from protobuf enum value: REQUEST_ENCODING_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + /** + * Uncompressed raw image data (e.g., raw RGB pixels, BMP, etc.) + * + * @generated from protobuf enum value: REQUEST_ENCODING_UNCOMPRESSED = 1; + */ + UNCOMPRESSED = 1, + /** + * Brotli-compressed image data for reduced bandwidth usage + * Server will decompress using Brotli algorithm before processing + * + * @generated from protobuf enum value: REQUEST_ENCODING_BROTLI = 2; + */ + BROTLI = 2 +} +/** + * Enumeration of supported image file formats. + * Specifies the image file format structure for proper parsing. + * + * @generated from protobuf enum athena.ImageFormat + */ +export enum ImageFormat { + /** + * @generated from protobuf enum value: IMAGE_FORMAT_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + /** + * @generated from protobuf enum value: IMAGE_FORMAT_GIF = 1; + */ + GIF = 1, + /** + * Covers .jpeg, .jpg, .jpe extensions + * + * @generated from protobuf enum value: IMAGE_FORMAT_JPEG = 2; + */ + JPEG = 2, + /** + * @generated from protobuf enum value: IMAGE_FORMAT_BMP = 3; + */ + BMP = 3, + /** + * @generated from protobuf enum value: IMAGE_FORMAT_DIB = 4; + */ + DIB = 4, + /** + * @generated from protobuf enum value: IMAGE_FORMAT_PNG = 5; + */ + PNG = 5, + /** + * @generated from protobuf enum value: IMAGE_FORMAT_WEBP = 6; + */ + WEBP = 6, + /** + * @generated from protobuf enum value: IMAGE_FORMAT_PBM = 7; + */ + PBM = 7, + /** + * @generated from protobuf enum value: IMAGE_FORMAT_PGM = 8; + */ + PGM = 8, + /** + * @generated from protobuf enum value: IMAGE_FORMAT_PPM = 9; + */ + PPM = 9, + /** + * @generated from protobuf enum value: IMAGE_FORMAT_PXM = 10; + */ + PXM = 10, + /** + * @generated from protobuf enum value: IMAGE_FORMAT_PNM = 11; + */ + PNM = 11, + /** + * @generated from protobuf enum value: IMAGE_FORMAT_PFM = 12; + */ + PFM = 12, + /** + * @generated from protobuf enum value: IMAGE_FORMAT_SR = 13; + */ + SR = 13, + /** + * @generated from protobuf enum value: IMAGE_FORMAT_RAS = 14; + */ + RAS = 14, + /** + * Covers .tiff, .tif extensions + * + * @generated from protobuf enum value: IMAGE_FORMAT_TIFF = 15; + */ + TIFF = 15, + /** + * @generated from protobuf enum value: IMAGE_FORMAT_HDR = 16; + */ + HDR = 16, + /** + * @generated from protobuf enum value: IMAGE_FORMAT_PIC = 17; + */ + PIC = 17, + /** + * Raw unsigned 8-bit image data (RGB, C order array) + * + * @generated from protobuf enum value: IMAGE_FORMAT_RAW_UINT8 = 18; + */ + RAW_UINT8 = 18 +} +/** + * Enumeration of hash types supported for image hashing. + * + * @generated from protobuf enum athena.HashType + */ +export enum HashType { + /** + * @generated from protobuf enum value: HASH_TYPE_UNKNOWN = 0; + */ + UNKNOWN = 0, + /** + * @generated from protobuf enum value: HASH_TYPE_MD5 = 1; + */ + MD5 = 1, + /** + * @generated from protobuf enum value: HASH_TYPE_SHA1 = 2; + */ + SHA1 = 2 +} +// @generated message type with reflection information, may provide speed optimized methods +class ListDeploymentsResponse$Type extends MessageType { + constructor() { + super("athena.ListDeploymentsResponse", [ + { no: 1, name: "deployments", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => Deployment } + ]); + } + create(value?: PartialMessage): ListDeploymentsResponse { + const message = globalThis.Object.create((this.messagePrototype!)); + message.deployments = []; + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ListDeploymentsResponse): ListDeploymentsResponse { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* repeated athena.Deployment deployments */ 1: + message.deployments.push(Deployment.internalBinaryRead(reader, reader.uint32(), options)); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: ListDeploymentsResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* repeated athena.Deployment deployments = 1; */ + for (let i = 0; i < message.deployments.length; i++) + Deployment.internalBinaryWrite(message.deployments[i], writer.tag(1, WireType.LengthDelimited).fork(), options).join(); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message athena.ListDeploymentsResponse + */ +export const ListDeploymentsResponse = new ListDeploymentsResponse$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class Deployment$Type extends MessageType { + constructor() { + super("athena.Deployment", [ + { no: 1, name: "deployment_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "backlog", kind: "scalar", T: 5 /*ScalarType.INT32*/ } + ]); + } + create(value?: PartialMessage): Deployment { + const message = globalThis.Object.create((this.messagePrototype!)); + message.deploymentId = ""; + message.backlog = 0; + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: Deployment): Deployment { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string deployment_id */ 1: + message.deploymentId = reader.string(); + break; + case /* int32 backlog */ 2: + message.backlog = reader.int32(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: Deployment, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string deployment_id = 1; */ + if (message.deploymentId !== "") + writer.tag(1, WireType.LengthDelimited).string(message.deploymentId); + /* int32 backlog = 2; */ + if (message.backlog !== 0) + writer.tag(2, WireType.Varint).int32(message.backlog); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message athena.Deployment + */ +export const Deployment = new Deployment$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class ClassifyRequest$Type extends MessageType { + constructor() { + super("athena.ClassifyRequest", [ + { no: 1, name: "deployment_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "inputs", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => ClassificationInput } + ]); + } + create(value?: PartialMessage): ClassifyRequest { + const message = globalThis.Object.create((this.messagePrototype!)); + message.deploymentId = ""; + message.inputs = []; + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ClassifyRequest): ClassifyRequest { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string deployment_id */ 1: + message.deploymentId = reader.string(); + break; + case /* repeated athena.ClassificationInput inputs */ 2: + message.inputs.push(ClassificationInput.internalBinaryRead(reader, reader.uint32(), options)); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: ClassifyRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string deployment_id = 1; */ + if (message.deploymentId !== "") + writer.tag(1, WireType.LengthDelimited).string(message.deploymentId); + /* repeated athena.ClassificationInput inputs = 2; */ + for (let i = 0; i < message.inputs.length; i++) + ClassificationInput.internalBinaryWrite(message.inputs[i], writer.tag(2, WireType.LengthDelimited).fork(), options).join(); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message athena.ClassifyRequest + */ +export const ClassifyRequest = new ClassifyRequest$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class ClassificationInput$Type extends MessageType { + constructor() { + super("athena.ClassificationInput", [ + { no: 1, name: "affiliate", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "correlation_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: "encoding", kind: "enum", T: () => ["athena.RequestEncoding", RequestEncoding, "REQUEST_ENCODING_"] }, + { no: 4, name: "data", kind: "scalar", T: 12 /*ScalarType.BYTES*/ }, + { no: 5, name: "format", kind: "enum", T: () => ["athena.ImageFormat", ImageFormat, "IMAGE_FORMAT_"] }, + { no: 6, name: "hashes", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => ImageHash } + ]); + } + create(value?: PartialMessage): ClassificationInput { + const message = globalThis.Object.create((this.messagePrototype!)); + message.affiliate = ""; + message.correlationId = ""; + message.encoding = 0; + message.data = new Uint8Array(0); + message.format = 0; + message.hashes = []; + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ClassificationInput): ClassificationInput { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string affiliate */ 1: + message.affiliate = reader.string(); + break; + case /* string correlation_id */ 2: + message.correlationId = reader.string(); + break; + case /* athena.RequestEncoding encoding */ 3: + message.encoding = reader.int32(); + break; + case /* bytes data */ 4: + message.data = reader.bytes(); + break; + case /* athena.ImageFormat format */ 5: + message.format = reader.int32(); + break; + case /* repeated athena.ImageHash hashes */ 6: + message.hashes.push(ImageHash.internalBinaryRead(reader, reader.uint32(), options)); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: ClassificationInput, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string affiliate = 1; */ + if (message.affiliate !== "") + writer.tag(1, WireType.LengthDelimited).string(message.affiliate); + /* string correlation_id = 2; */ + if (message.correlationId !== "") + writer.tag(2, WireType.LengthDelimited).string(message.correlationId); + /* athena.RequestEncoding encoding = 3; */ + if (message.encoding !== 0) + writer.tag(3, WireType.Varint).int32(message.encoding); + /* bytes data = 4; */ + if (message.data.length) + writer.tag(4, WireType.LengthDelimited).bytes(message.data); + /* athena.ImageFormat format = 5; */ + if (message.format !== 0) + writer.tag(5, WireType.Varint).int32(message.format); + /* repeated athena.ImageHash hashes = 6; */ + for (let i = 0; i < message.hashes.length; i++) + ImageHash.internalBinaryWrite(message.hashes[i], writer.tag(6, WireType.LengthDelimited).fork(), options).join(); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message athena.ClassificationInput + */ +export const ClassificationInput = new ClassificationInput$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class ClassifyResponse$Type extends MessageType { + constructor() { + super("athena.ClassifyResponse", [ + { no: 1, name: "global_error", kind: "message", T: () => ClassificationError }, + { no: 2, name: "outputs", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => ClassificationOutput } + ]); + } + create(value?: PartialMessage): ClassifyResponse { + const message = globalThis.Object.create((this.messagePrototype!)); + message.outputs = []; + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ClassifyResponse): ClassifyResponse { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* athena.ClassificationError global_error */ 1: + message.globalError = ClassificationError.internalBinaryRead(reader, reader.uint32(), options, message.globalError); + break; + case /* repeated athena.ClassificationOutput outputs */ 2: + message.outputs.push(ClassificationOutput.internalBinaryRead(reader, reader.uint32(), options)); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: ClassifyResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* athena.ClassificationError global_error = 1; */ + if (message.globalError) + ClassificationError.internalBinaryWrite(message.globalError, writer.tag(1, WireType.LengthDelimited).fork(), options).join(); + /* repeated athena.ClassificationOutput outputs = 2; */ + for (let i = 0; i < message.outputs.length; i++) + ClassificationOutput.internalBinaryWrite(message.outputs[i], writer.tag(2, WireType.LengthDelimited).fork(), options).join(); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message athena.ClassifyResponse + */ +export const ClassifyResponse = new ClassifyResponse$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class ClassificationOutput$Type extends MessageType { + constructor() { + super("athena.ClassificationOutput", [ + { no: 1, name: "correlation_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "classifications", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => Classification }, + { no: 3, name: "error", kind: "message", T: () => ClassificationError } + ]); + } + create(value?: PartialMessage): ClassificationOutput { + const message = globalThis.Object.create((this.messagePrototype!)); + message.correlationId = ""; + message.classifications = []; + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ClassificationOutput): ClassificationOutput { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string correlation_id */ 1: + message.correlationId = reader.string(); + break; + case /* repeated athena.Classification classifications */ 2: + message.classifications.push(Classification.internalBinaryRead(reader, reader.uint32(), options)); + break; + case /* athena.ClassificationError error */ 3: + message.error = ClassificationError.internalBinaryRead(reader, reader.uint32(), options, message.error); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: ClassificationOutput, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string correlation_id = 1; */ + if (message.correlationId !== "") + writer.tag(1, WireType.LengthDelimited).string(message.correlationId); + /* repeated athena.Classification classifications = 2; */ + for (let i = 0; i < message.classifications.length; i++) + Classification.internalBinaryWrite(message.classifications[i], writer.tag(2, WireType.LengthDelimited).fork(), options).join(); + /* athena.ClassificationError error = 3; */ + if (message.error) + ClassificationError.internalBinaryWrite(message.error, writer.tag(3, WireType.LengthDelimited).fork(), options).join(); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message athena.ClassificationOutput + */ +export const ClassificationOutput = new ClassificationOutput$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class Classification$Type extends MessageType { + constructor() { + super("athena.Classification", [ + { no: 1, name: "label", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "weight", kind: "scalar", T: 2 /*ScalarType.FLOAT*/ } + ]); + } + create(value?: PartialMessage): Classification { + const message = globalThis.Object.create((this.messagePrototype!)); + message.label = ""; + message.weight = 0; + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: Classification): Classification { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string label */ 1: + message.label = reader.string(); + break; + case /* float weight */ 2: + message.weight = reader.float(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: Classification, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string label = 1; */ + if (message.label !== "") + writer.tag(1, WireType.LengthDelimited).string(message.label); + /* float weight = 2; */ + if (message.weight !== 0) + writer.tag(2, WireType.Bit32).float(message.weight); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message athena.Classification + */ +export const Classification = new Classification$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class ClassificationError$Type extends MessageType { + constructor() { + super("athena.ClassificationError", [ + { no: 1, name: "code", kind: "enum", T: () => ["athena.ErrorCode", ErrorCode, "ERROR_CODE_"] }, + { no: 2, name: "message", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: "details", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + ]); + } + create(value?: PartialMessage): ClassificationError { + const message = globalThis.Object.create((this.messagePrototype!)); + message.code = 0; + message.message = ""; + message.details = ""; + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ClassificationError): ClassificationError { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* athena.ErrorCode code */ 1: + message.code = reader.int32(); + break; + case /* string message */ 2: + message.message = reader.string(); + break; + case /* string details */ 3: + message.details = reader.string(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: ClassificationError, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* athena.ErrorCode code = 1; */ + if (message.code !== 0) + writer.tag(1, WireType.Varint).int32(message.code); + /* string message = 2; */ + if (message.message !== "") + writer.tag(2, WireType.LengthDelimited).string(message.message); + /* string details = 3; */ + if (message.details !== "") + writer.tag(3, WireType.LengthDelimited).string(message.details); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message athena.ClassificationError + */ +export const ClassificationError = new ClassificationError$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class ImageHash$Type extends MessageType { + constructor() { + super("athena.ImageHash", [ + { no: 1, name: "value", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "type", kind: "enum", T: () => ["athena.HashType", HashType, "HASH_TYPE_"] } + ]); + } + create(value?: PartialMessage): ImageHash { + const message = globalThis.Object.create((this.messagePrototype!)); + message.value = ""; + message.type = 0; + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ImageHash): ImageHash { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string value */ 1: + message.value = reader.string(); + break; + case /* athena.HashType type */ 2: + message.type = reader.int32(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: ImageHash, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string value = 1; */ + if (message.value !== "") + writer.tag(1, WireType.LengthDelimited).string(message.value); + /* athena.HashType type = 2; */ + if (message.type !== 0) + writer.tag(2, WireType.Varint).int32(message.type); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message athena.ImageHash + */ +export const ImageHash = new ImageHash$Type(); diff --git a/src/athena/google/protobuf/empty.ts b/src/generated/google/protobuf/empty.ts similarity index 56% rename from src/athena/google/protobuf/empty.ts rename to src/generated/google/protobuf/empty.ts index 3a9e890..f4271a3 100644 --- a/src/athena/google/protobuf/empty.ts +++ b/src/generated/google/protobuf/empty.ts @@ -32,14 +32,14 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -import type { BinaryWriteOptions } from '@protobuf-ts/runtime'; -import type { IBinaryWriter } from '@protobuf-ts/runtime'; -import type { BinaryReadOptions } from '@protobuf-ts/runtime'; -import type { IBinaryReader } from '@protobuf-ts/runtime'; -import { UnknownFieldHandler } from '@protobuf-ts/runtime'; -import type { PartialMessage } from '@protobuf-ts/runtime'; -import { reflectionMergePartial } from '@protobuf-ts/runtime'; -import { MessageType } from '@protobuf-ts/runtime'; +import type { BinaryWriteOptions } from "@protobuf-ts/runtime"; +import type { IBinaryWriter } from "@protobuf-ts/runtime"; +import type { BinaryReadOptions } from "@protobuf-ts/runtime"; +import type { IBinaryReader } from "@protobuf-ts/runtime"; +import { UnknownFieldHandler } from "@protobuf-ts/runtime"; +import type { PartialMessage } from "@protobuf-ts/runtime"; +import { reflectionMergePartial } from "@protobuf-ts/runtime"; +import { MessageType } from "@protobuf-ts/runtime"; /** * A generic empty message that you can re-use to avoid defining duplicated * empty messages in your APIs. A typical example is to use it as the request @@ -52,62 +52,41 @@ import { MessageType } from '@protobuf-ts/runtime'; * * @generated from protobuf message google.protobuf.Empty */ -export interface Empty {} +export interface Empty { +} // @generated message type with reflection information, may provide speed optimized methods class Empty$Type extends MessageType { - constructor() { - super('google.protobuf.Empty', []); - } - create(value?: PartialMessage): Empty { - const message = globalThis.Object.create(this.messagePrototype!); - if (value !== undefined) - reflectionMergePartial(this, message, value); - return message; - } - internalBinaryRead( - reader: IBinaryReader, - length: number, - options: BinaryReadOptions, - target?: Empty, - ): Empty { - let message = target ?? this.create(), - end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - default: - let u = options.readUnknownField; - if (u === 'throw') - throw new globalThis.Error( - `Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`, - ); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)( - this.typeName, - message, - fieldNo, - wireType, - d, - ); - } + constructor() { + super("google.protobuf.Empty", []); + } + create(value?: PartialMessage): Empty { + const message = globalThis.Object.create((this.messagePrototype!)); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: Empty): Empty { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: Empty, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; } - return message; - } - internalBinaryWrite( - message: Empty, - writer: IBinaryWriter, - options: BinaryWriteOptions, - ): IBinaryWriter { - let u = options.writeUnknownFields; - if (u !== false) - (u == true ? UnknownFieldHandler.onWrite : u)( - this.typeName, - message, - writer, - ); - return writer; - } } /** * @generated MessageType for protobuf message google.protobuf.Empty diff --git a/src/hashing.ts b/src/hashing.ts index 53a9dc3..8a4c8c6 100644 --- a/src/hashing.ts +++ b/src/hashing.ts @@ -1,7 +1,11 @@ import { Readable } from 'stream'; import crypto from 'crypto'; import sharp from 'sharp'; -import { HashType, ImageFormat, RequestEncoding } from '.'; +import { + HashType, + ImageFormat, + RequestEncoding, +} from './generated/athena/models'; import brotli from 'brotli'; import { buffer } from 'stream/consumers'; diff --git a/src/index.ts b/src/index.ts index 80425f3..cda3b34 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,14 +9,14 @@ import { ClassifyResponse, Deployment, ImageFormat, -} from './athena/athena'; +} from './generated/athena/models'; import * as grpc from '@grpc/grpc-js'; import { type IClassifierServiceClient, ClassifierServiceClient, -} from './athena/athena.grpc-client'; +} from './generated/athena/athena.grpc-client'; import { EventEmitter } from 'events'; -import { Empty } from './athena/google/protobuf/empty'; +import { Empty } from './generated/google/protobuf/empty'; import { type AuthenticationOptions, AuthenticationManager, @@ -345,6 +345,7 @@ export class ClassifierSdk extends (EventEmitter as new () => TypedEventEmitter< } } -export * from './athena/athena'; -export * from './athena/athena.grpc-client'; +export * from './generated/athena/models'; +export * from './generated/athena/athena'; +export * from './generated/athena/athena.grpc-client'; export * from './hashing'; From 76d76eda7c72feb6bf170b9cd76654a0d53bfd30 Mon Sep 17 00:00:00 2001 From: snus-kin Date: Wed, 10 Sep 2025 16:10:10 +0100 Subject: [PATCH 2/8] ci: codegen in the pipeline --- .github/workflows/functional-tests.yml | 3 + .github/workflows/nodejs.yml | 3 + .github/workflows/release.yml | 5 +- .gitignore | 2 + package.json | 2 +- src/generated/athena/athena.grpc-client.ts | 120 --- src/generated/athena/athena.ts | 18 - src/generated/athena/models.ts | 952 --------------------- src/generated/google/protobuf/empty.ts | 94 -- 9 files changed, 12 insertions(+), 1187 deletions(-) delete mode 100644 src/generated/athena/athena.grpc-client.ts delete mode 100644 src/generated/athena/athena.ts delete mode 100644 src/generated/athena/models.ts delete mode 100644 src/generated/google/protobuf/empty.ts diff --git a/.github/workflows/functional-tests.yml b/.github/workflows/functional-tests.yml index 8ed85f0..2bb2c71 100644 --- a/.github/workflows/functional-tests.yml +++ b/.github/workflows/functional-tests.yml @@ -35,6 +35,9 @@ jobs: - name: Install dependencies run: npm ci --no-audit + - name: Generate code + run: npm run codegen + - name: Build project run: npm run build --if-present diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index a9a2833..5e924ac 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -19,6 +19,9 @@ jobs: - name: Install dependencies run: npm ci --no-audit + - name: Generate code + run: npm run codegen + - name: Run linting run: npm run lint --if-present diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1be7cea..4f8e329 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,11 +10,12 @@ jobs: - uses: actions/checkout@v5 - uses: actions/setup-node@v5 with: - node-version: "22" + node-version: '22' - run: npm ci + - run: npm run codegen - run: npm run build:release - uses: JS-DevTools/npm-publish@v3 with: token: ${{ secrets.GITHUB_TOKEN }} - registry: "https://npm.pkg.github.com" + registry: 'https://npm.pkg.github.com' access: restricted diff --git a/.gitignore b/.gitignore index 711351d..ab0c42a 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,5 @@ dist/ .env .venv + +src/generated diff --git a/package.json b/package.json index faccc01..baf405c 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "build": "tsc -p tsconfig.json", "build:watch": "tsc -w -p tsconfig.json", "build:release": "npm run clean && tsc -p tsconfig.release.json", - "codegen": "npx protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --ts_out=client_grpc1:./src/generated --proto_path=./athena-protobufs ./athena-protobufs/athena/**.proto", + "codegen": "mkdir -p src/generated && npx protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --ts_out=client_grpc1:./src/generated --proto_path=./athena-protobufs ./athena-protobufs/athena/**.proto", "lint": "eslint .", "lint:all": "npm run lint && npm run prettier:check && npx tsc --noEmit", "precommit:setup": "echo 'To install pre-commit hooks, run: uv tool install pre-commit && uvx pre-commit install'", diff --git a/src/generated/athena/athena.grpc-client.ts b/src/generated/athena/athena.grpc-client.ts deleted file mode 100644 index df24522..0000000 --- a/src/generated/athena/athena.grpc-client.ts +++ /dev/null @@ -1,120 +0,0 @@ -// @generated by protobuf-ts 2.11.1 with parameter client_grpc1 -// @generated from protobuf file "athena/athena.proto" (package "athena", syntax proto3) -// tslint:disable -import { ClassifierService } from "./athena"; -import type { BinaryWriteOptions } from "@protobuf-ts/runtime"; -import type { BinaryReadOptions } from "@protobuf-ts/runtime"; -import type { ClassificationOutput } from "./models"; -import type { ClassificationInput } from "./models"; -import type { ListDeploymentsResponse } from "./models"; -import type { Empty } from "../google/protobuf/empty"; -import type { ClassifyResponse } from "./models"; -import type { ClassifyRequest } from "./models"; -import * as grpc from "@grpc/grpc-js"; -/** - * The classifier service definition. - * Provides image classification capabilities with session-based streaming - * and client management functionality. - * - * @generated from protobuf service athena.ClassifierService - */ -export interface IClassifierServiceClient { - /** - * Classify images in a deployment-based streaming context - * Multiple affiliates can join the same deployment to share responses - * Supports bidirectional streaming for real-time classification - * - * @generated from protobuf rpc: Classify - */ - classify(metadata: grpc.Metadata, options?: grpc.CallOptions): grpc.ClientDuplexStream; - classify(options?: grpc.CallOptions): grpc.ClientDuplexStream; - /** - * Retrieves a list of all active deployment IDs - * Returns the active deployment_id values that can be used in Classify requests - * Useful for monitoring and debugging active connections - * - * @generated from protobuf rpc: ListDeployments - */ - listDeployments(input: Empty, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: ListDeploymentsResponse) => void): grpc.ClientUnaryCall; - listDeployments(input: Empty, metadata: grpc.Metadata, callback: (err: grpc.ServiceError | null, value?: ListDeploymentsResponse) => void): grpc.ClientUnaryCall; - listDeployments(input: Empty, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: ListDeploymentsResponse) => void): grpc.ClientUnaryCall; - listDeployments(input: Empty, callback: (err: grpc.ServiceError | null, value?: ListDeploymentsResponse) => void): grpc.ClientUnaryCall; - /** - * Classifies a single image synchronously without deployment context - * Returns classification results immediately in a single request-response cycle - * Unlike the streaming Classify method, this operates independently of deployments - * and does not require session management or deployment coordination - * - * Use this for: - * - Low-throughput, low-latency classification scenarios - * - Simple one-off image classifications - * - Applications where immediate synchronous responses are preferred over streaming - * - Testing and debugging individual image classifications - * - * The response will contain either classification results or error information - * No deployment_id coordination is required or supported - * - * @generated from protobuf rpc: ClassifySingle - */ - classifySingle(input: ClassificationInput, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: ClassificationOutput) => void): grpc.ClientUnaryCall; - classifySingle(input: ClassificationInput, metadata: grpc.Metadata, callback: (err: grpc.ServiceError | null, value?: ClassificationOutput) => void): grpc.ClientUnaryCall; - classifySingle(input: ClassificationInput, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: ClassificationOutput) => void): grpc.ClientUnaryCall; - classifySingle(input: ClassificationInput, callback: (err: grpc.ServiceError | null, value?: ClassificationOutput) => void): grpc.ClientUnaryCall; -} -/** - * The classifier service definition. - * Provides image classification capabilities with session-based streaming - * and client management functionality. - * - * @generated from protobuf service athena.ClassifierService - */ -export class ClassifierServiceClient extends grpc.Client implements IClassifierServiceClient { - private readonly _binaryOptions: Partial; - constructor(address: string, credentials: grpc.ChannelCredentials, options: grpc.ClientOptions = {}, binaryOptions: Partial = {}) { - super(address, credentials, options); - this._binaryOptions = binaryOptions; - } - /** - * Classify images in a deployment-based streaming context - * Multiple affiliates can join the same deployment to share responses - * Supports bidirectional streaming for real-time classification - * - * @generated from protobuf rpc: Classify - */ - classify(metadata?: grpc.Metadata | grpc.CallOptions, options?: grpc.CallOptions): grpc.ClientDuplexStream { - const method = ClassifierService.methods[0]; - return this.makeBidiStreamRequest(`/${ClassifierService.typeName}/${method.name}`, (value: ClassifyRequest): Buffer => Buffer.from(method.I.toBinary(value, this._binaryOptions)), (value: Buffer): ClassifyResponse => method.O.fromBinary(value, this._binaryOptions), (metadata as any), options); - } - /** - * Retrieves a list of all active deployment IDs - * Returns the active deployment_id values that can be used in Classify requests - * Useful for monitoring and debugging active connections - * - * @generated from protobuf rpc: ListDeployments - */ - listDeployments(input: Empty, metadata: grpc.Metadata | grpc.CallOptions | ((err: grpc.ServiceError | null, value?: ListDeploymentsResponse) => void), options?: grpc.CallOptions | ((err: grpc.ServiceError | null, value?: ListDeploymentsResponse) => void), callback?: ((err: grpc.ServiceError | null, value?: ListDeploymentsResponse) => void)): grpc.ClientUnaryCall { - const method = ClassifierService.methods[1]; - return this.makeUnaryRequest(`/${ClassifierService.typeName}/${method.name}`, (value: Empty): Buffer => Buffer.from(method.I.toBinary(value, this._binaryOptions)), (value: Buffer): ListDeploymentsResponse => method.O.fromBinary(value, this._binaryOptions), input, (metadata as any), (options as any), (callback as any)); - } - /** - * Classifies a single image synchronously without deployment context - * Returns classification results immediately in a single request-response cycle - * Unlike the streaming Classify method, this operates independently of deployments - * and does not require session management or deployment coordination - * - * Use this for: - * - Low-throughput, low-latency classification scenarios - * - Simple one-off image classifications - * - Applications where immediate synchronous responses are preferred over streaming - * - Testing and debugging individual image classifications - * - * The response will contain either classification results or error information - * No deployment_id coordination is required or supported - * - * @generated from protobuf rpc: ClassifySingle - */ - classifySingle(input: ClassificationInput, metadata: grpc.Metadata | grpc.CallOptions | ((err: grpc.ServiceError | null, value?: ClassificationOutput) => void), options?: grpc.CallOptions | ((err: grpc.ServiceError | null, value?: ClassificationOutput) => void), callback?: ((err: grpc.ServiceError | null, value?: ClassificationOutput) => void)): grpc.ClientUnaryCall { - const method = ClassifierService.methods[2]; - return this.makeUnaryRequest(`/${ClassifierService.typeName}/${method.name}`, (value: ClassificationInput): Buffer => Buffer.from(method.I.toBinary(value, this._binaryOptions)), (value: Buffer): ClassificationOutput => method.O.fromBinary(value, this._binaryOptions), input, (metadata as any), (options as any), (callback as any)); - } -} diff --git a/src/generated/athena/athena.ts b/src/generated/athena/athena.ts deleted file mode 100644 index 8624373..0000000 --- a/src/generated/athena/athena.ts +++ /dev/null @@ -1,18 +0,0 @@ -// @generated by protobuf-ts 2.11.1 with parameter client_grpc1 -// @generated from protobuf file "athena/athena.proto" (package "athena", syntax proto3) -// tslint:disable -import { ClassificationOutput } from "./models"; -import { ClassificationInput } from "./models"; -import { ListDeploymentsResponse } from "./models"; -import { Empty } from "../google/protobuf/empty"; -import { ClassifyResponse } from "./models"; -import { ClassifyRequest } from "./models"; -import { ServiceType } from "@protobuf-ts/runtime-rpc"; -/** - * @generated ServiceType for protobuf service athena.ClassifierService - */ -export const ClassifierService = new ServiceType("athena.ClassifierService", [ - { name: "Classify", serverStreaming: true, clientStreaming: true, options: {}, I: ClassifyRequest, O: ClassifyResponse }, - { name: "ListDeployments", options: {}, I: Empty, O: ListDeploymentsResponse }, - { name: "ClassifySingle", options: {}, I: ClassificationInput, O: ClassificationOutput } -]); diff --git a/src/generated/athena/models.ts b/src/generated/athena/models.ts deleted file mode 100644 index ae6fc36..0000000 --- a/src/generated/athena/models.ts +++ /dev/null @@ -1,952 +0,0 @@ -// @generated by protobuf-ts 2.11.1 with parameter client_grpc1 -// @generated from protobuf file "athena/models.proto" (package "athena", syntax proto3) -// tslint:disable -import type { BinaryWriteOptions } from "@protobuf-ts/runtime"; -import type { IBinaryWriter } from "@protobuf-ts/runtime"; -import { WireType } from "@protobuf-ts/runtime"; -import type { BinaryReadOptions } from "@protobuf-ts/runtime"; -import type { IBinaryReader } from "@protobuf-ts/runtime"; -import { UnknownFieldHandler } from "@protobuf-ts/runtime"; -import type { PartialMessage } from "@protobuf-ts/runtime"; -import { reflectionMergePartial } from "@protobuf-ts/runtime"; -import { MessageType } from "@protobuf-ts/runtime"; -/** - * Response message for ListDeployments RPC - * Contains the list of active deployments and their details. - * - * @generated from protobuf message athena.ListDeploymentsResponse - */ -export interface ListDeploymentsResponse { - /** - * List of active deployments with their backlog information. - * - * @generated from protobuf field: repeated athena.Deployment deployments = 1 - */ - deployments: Deployment[]; -} -/** - * A single active deployment part of a `ListDeployments` response - * - * @generated from protobuf message athena.Deployment - */ -export interface Deployment { - /** - * active deployment identifier - * - * @generated from protobuf field: string deployment_id = 1 - */ - deploymentId: string; - /** - * Backlog of classification responses in this deployment - * - * @generated from protobuf field: int32 backlog = 2 - */ - backlog: number; -} -/** - * The request message containing the image data to classify. - * Each request represents a batch of images that should be processed within - * the same deployment context. - * - * @generated from protobuf message athena.ClassifyRequest - */ -export interface ClassifyRequest { - /** - * Client's unique identifier for this deployment. Responses returned will be - * sent to a client with a matching deployment_id. - * - * @generated from protobuf field: string deployment_id = 1 - */ - deploymentId: string; - /** - * Array of images to be classified in this request batch - * Allows sending multiple images in a single request for efficiency. - * - * @generated from protobuf field: repeated athena.ClassificationInput inputs = 2 - */ - inputs: ClassificationInput[]; -} -/** - * A single image within a classification request batch. - * Contains all necessary metadata and data for classifying one image. - * - * @generated from protobuf message athena.ClassificationInput - */ -export interface ClassificationInput { - /** - * The affiliate or source system that provided this image - * Used for tracking, analytics, and routing purposes. - * - * @generated from protobuf field: string affiliate = 1 - */ - affiliate: string; - /** - * Unique identifier for correlating this input with its response - * Must be unique within the deployment to properly match responses - * - * @generated from protobuf field: string correlation_id = 2 - */ - correlationId: string; - /** - * Specifies the encoding/compression format of the image data - * Allows the server to properly decode the image before classification - * - * @generated from protobuf field: athena.RequestEncoding encoding = 3 - */ - encoding: RequestEncoding; - /** - * The raw image data bytes in the format specified by encoding - * Can be compressed or uncompressed based on the encoding field - * - * @generated from protobuf field: bytes data = 4 - */ - data: Uint8Array; - /** - * The image file format of the data bytes - * - * @generated from protobuf field: athena.ImageFormat format = 5 - */ - format: ImageFormat; - /** - * Hashes of the image data, can be multiple depending on image processing - * (e.g. of raw bytes, jpeg image, etc.) but must be for a single image. - * - * @generated from protobuf field: repeated athena.ImageHash hashes = 6 - */ - hashes: ImageHash[]; -} -/** - * The response message containing the classification results. - * Sent back to clients for each processed batch, containing either - * a global error or individual results for each image in the batch. - * - * @generated from protobuf message athena.ClassifyResponse - */ -export interface ClassifyResponse { - /** - * Global error affecting the entire batch/request - * If present, indicates that the entire request failed and no individual - * image results will be provided - * - * @generated from protobuf field: athena.ClassificationError global_error = 1 - */ - globalError?: ClassificationError; - /** - * Array of classification results, one for each input image - * Will be empty if global_error is present - * - * @generated from protobuf field: repeated athena.ClassificationOutput outputs = 2 - */ - outputs: ClassificationOutput[]; -} -/** - * Individual classification result for a single image. - * Contains the correlation ID and classification results for one image. - * - * @generated from protobuf message athena.ClassificationOutput - */ -export interface ClassificationOutput { - /** - * Matches the correlationId from the corresponding ClassificationInput - * Allows clients to match responses with their original requests - * - * @generated from protobuf field: string correlation_id = 1 - */ - correlationId: string; - /** - * Array of all classifications detected for this image - * Multiple classifications may be returned with different confidence levels - * - * @generated from protobuf field: repeated athena.Classification classifications = 2 - */ - classifications: Classification[]; - /** - * Error information if this specific image classification failed - * If present, indicates that this particular image could not be processed - * - * @generated from protobuf field: athena.ClassificationError error = 3 - */ - error?: ClassificationError; -} -/** - * A single classification result for an image. - * Represents one detected category with its confidence score. - * - * @generated from protobuf message athena.Classification - */ -export interface Classification { - /** - * Human-readable label describing what was classified - * Examples: "CatA", "CatB", "Indicative", "Distraction", etc. - * - * @generated from protobuf field: string label = 1 - */ - label: string; - /** - * Confidence score between 0.0 and 1.0 indicating certainty - * Higher values indicate greater confidence in the classification - * - * @generated from protobuf field: float weight = 2 - */ - weight: number; -} -/** - * Error information for failed classification attempts. - * Provides details about why a classification could not be completed. - * - * @generated from protobuf message athena.ClassificationError - */ -export interface ClassificationError { - /** - * Error code indicating the type of failure - * - * @generated from protobuf field: athena.ErrorCode code = 1 - */ - code: ErrorCode; - /** - * Human-readable error message providing details about the failure - * - * @generated from protobuf field: string message = 2 - */ - message: string; - /** - * Additional context or details about the error (optional) - * - * @generated from protobuf field: string details = 3 - */ - details: string; -} -/** - * Hash information for an image. - * - * @generated from protobuf message athena.ImageHash - */ -export interface ImageHash { - /** - * The hash value of the image data - * - * @generated from protobuf field: string value = 1 - */ - value: string; - /** - * The type of hash algorithm used to generate the hash value - * - * @generated from protobuf field: athena.HashType type = 2 - */ - type: HashType; -} -/** - * Enumeration of possible classification error codes. - * - * @generated from protobuf enum athena.ErrorCode - */ -export enum ErrorCode { - /** - * Unknown or unspecified error - * - * @generated from protobuf enum value: ERROR_CODE_UNSPECIFIED = 0; - */ - UNSPECIFIED = 0, - /** - * Image is too large to process - * - * @generated from protobuf enum value: ERROR_CODE_IMAGE_TOO_LARGE = 2; - */ - IMAGE_TOO_LARGE = 2, - /** - * Opaque error from the classifier - * - * @generated from protobuf enum value: ERROR_CODE_MODEL_ERROR = 3; - */ - MODEL_ERROR = 3, - /** - * Attempt to send data for an affiliate which is not granted to the current - * client. - * - * @generated from protobuf enum value: ERROR_CODE_AFFILIATE_NOT_PERMITTED = 4; - */ - AFFILIATE_NOT_PERMITTED = 4, - /** - * The format of the deployment ID is not valid. Deployment IDs must be - * non-empty strings of up to 64 characters. - * - * @generated from protobuf enum value: ERROR_CODE_DEPLOYMENT_ID_INVALID = 5; - */ - DEPLOYMENT_ID_INVALID = 5, - /** - * The format of the correlation ID is not valid. Correlation IDs must be - * non-empty strings of up to 64 characters. - * - * @generated from protobuf enum value: ERROR_CODE_CORRELATION_ID_INVALID = 6; - */ - CORRELATION_ID_INVALID = 6 -} -/** - * Enumeration of supported image data encoding formats. - * Determines how the server should interpret the image data bytes. - * - * @generated from protobuf enum athena.RequestEncoding - */ -export enum RequestEncoding { - /** - * Unspecified encoding. Assumed to be uncompressed. - * - * @generated from protobuf enum value: REQUEST_ENCODING_UNSPECIFIED = 0; - */ - UNSPECIFIED = 0, - /** - * Uncompressed raw image data (e.g., raw RGB pixels, BMP, etc.) - * - * @generated from protobuf enum value: REQUEST_ENCODING_UNCOMPRESSED = 1; - */ - UNCOMPRESSED = 1, - /** - * Brotli-compressed image data for reduced bandwidth usage - * Server will decompress using Brotli algorithm before processing - * - * @generated from protobuf enum value: REQUEST_ENCODING_BROTLI = 2; - */ - BROTLI = 2 -} -/** - * Enumeration of supported image file formats. - * Specifies the image file format structure for proper parsing. - * - * @generated from protobuf enum athena.ImageFormat - */ -export enum ImageFormat { - /** - * @generated from protobuf enum value: IMAGE_FORMAT_UNSPECIFIED = 0; - */ - UNSPECIFIED = 0, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_GIF = 1; - */ - GIF = 1, - /** - * Covers .jpeg, .jpg, .jpe extensions - * - * @generated from protobuf enum value: IMAGE_FORMAT_JPEG = 2; - */ - JPEG = 2, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_BMP = 3; - */ - BMP = 3, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_DIB = 4; - */ - DIB = 4, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_PNG = 5; - */ - PNG = 5, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_WEBP = 6; - */ - WEBP = 6, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_PBM = 7; - */ - PBM = 7, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_PGM = 8; - */ - PGM = 8, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_PPM = 9; - */ - PPM = 9, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_PXM = 10; - */ - PXM = 10, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_PNM = 11; - */ - PNM = 11, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_PFM = 12; - */ - PFM = 12, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_SR = 13; - */ - SR = 13, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_RAS = 14; - */ - RAS = 14, - /** - * Covers .tiff, .tif extensions - * - * @generated from protobuf enum value: IMAGE_FORMAT_TIFF = 15; - */ - TIFF = 15, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_HDR = 16; - */ - HDR = 16, - /** - * @generated from protobuf enum value: IMAGE_FORMAT_PIC = 17; - */ - PIC = 17, - /** - * Raw unsigned 8-bit image data (RGB, C order array) - * - * @generated from protobuf enum value: IMAGE_FORMAT_RAW_UINT8 = 18; - */ - RAW_UINT8 = 18 -} -/** - * Enumeration of hash types supported for image hashing. - * - * @generated from protobuf enum athena.HashType - */ -export enum HashType { - /** - * @generated from protobuf enum value: HASH_TYPE_UNKNOWN = 0; - */ - UNKNOWN = 0, - /** - * @generated from protobuf enum value: HASH_TYPE_MD5 = 1; - */ - MD5 = 1, - /** - * @generated from protobuf enum value: HASH_TYPE_SHA1 = 2; - */ - SHA1 = 2 -} -// @generated message type with reflection information, may provide speed optimized methods -class ListDeploymentsResponse$Type extends MessageType { - constructor() { - super("athena.ListDeploymentsResponse", [ - { no: 1, name: "deployments", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => Deployment } - ]); - } - create(value?: PartialMessage): ListDeploymentsResponse { - const message = globalThis.Object.create((this.messagePrototype!)); - message.deployments = []; - if (value !== undefined) - reflectionMergePartial(this, message, value); - return message; - } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ListDeploymentsResponse): ListDeploymentsResponse { - let message = target ?? this.create(), end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - case /* repeated athena.Deployment deployments */ 1: - message.deployments.push(Deployment.internalBinaryRead(reader, reader.uint32(), options)); - break; - default: - let u = options.readUnknownField; - if (u === "throw") - throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); - } - } - return message; - } - internalBinaryWrite(message: ListDeploymentsResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { - /* repeated athena.Deployment deployments = 1; */ - for (let i = 0; i < message.deployments.length; i++) - Deployment.internalBinaryWrite(message.deployments[i], writer.tag(1, WireType.LengthDelimited).fork(), options).join(); - let u = options.writeUnknownFields; - if (u !== false) - (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); - return writer; - } -} -/** - * @generated MessageType for protobuf message athena.ListDeploymentsResponse - */ -export const ListDeploymentsResponse = new ListDeploymentsResponse$Type(); -// @generated message type with reflection information, may provide speed optimized methods -class Deployment$Type extends MessageType { - constructor() { - super("athena.Deployment", [ - { no: 1, name: "deployment_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "backlog", kind: "scalar", T: 5 /*ScalarType.INT32*/ } - ]); - } - create(value?: PartialMessage): Deployment { - const message = globalThis.Object.create((this.messagePrototype!)); - message.deploymentId = ""; - message.backlog = 0; - if (value !== undefined) - reflectionMergePartial(this, message, value); - return message; - } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: Deployment): Deployment { - let message = target ?? this.create(), end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - case /* string deployment_id */ 1: - message.deploymentId = reader.string(); - break; - case /* int32 backlog */ 2: - message.backlog = reader.int32(); - break; - default: - let u = options.readUnknownField; - if (u === "throw") - throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); - } - } - return message; - } - internalBinaryWrite(message: Deployment, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { - /* string deployment_id = 1; */ - if (message.deploymentId !== "") - writer.tag(1, WireType.LengthDelimited).string(message.deploymentId); - /* int32 backlog = 2; */ - if (message.backlog !== 0) - writer.tag(2, WireType.Varint).int32(message.backlog); - let u = options.writeUnknownFields; - if (u !== false) - (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); - return writer; - } -} -/** - * @generated MessageType for protobuf message athena.Deployment - */ -export const Deployment = new Deployment$Type(); -// @generated message type with reflection information, may provide speed optimized methods -class ClassifyRequest$Type extends MessageType { - constructor() { - super("athena.ClassifyRequest", [ - { no: 1, name: "deployment_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "inputs", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => ClassificationInput } - ]); - } - create(value?: PartialMessage): ClassifyRequest { - const message = globalThis.Object.create((this.messagePrototype!)); - message.deploymentId = ""; - message.inputs = []; - if (value !== undefined) - reflectionMergePartial(this, message, value); - return message; - } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ClassifyRequest): ClassifyRequest { - let message = target ?? this.create(), end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - case /* string deployment_id */ 1: - message.deploymentId = reader.string(); - break; - case /* repeated athena.ClassificationInput inputs */ 2: - message.inputs.push(ClassificationInput.internalBinaryRead(reader, reader.uint32(), options)); - break; - default: - let u = options.readUnknownField; - if (u === "throw") - throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); - } - } - return message; - } - internalBinaryWrite(message: ClassifyRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { - /* string deployment_id = 1; */ - if (message.deploymentId !== "") - writer.tag(1, WireType.LengthDelimited).string(message.deploymentId); - /* repeated athena.ClassificationInput inputs = 2; */ - for (let i = 0; i < message.inputs.length; i++) - ClassificationInput.internalBinaryWrite(message.inputs[i], writer.tag(2, WireType.LengthDelimited).fork(), options).join(); - let u = options.writeUnknownFields; - if (u !== false) - (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); - return writer; - } -} -/** - * @generated MessageType for protobuf message athena.ClassifyRequest - */ -export const ClassifyRequest = new ClassifyRequest$Type(); -// @generated message type with reflection information, may provide speed optimized methods -class ClassificationInput$Type extends MessageType { - constructor() { - super("athena.ClassificationInput", [ - { no: 1, name: "affiliate", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "correlation_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 3, name: "encoding", kind: "enum", T: () => ["athena.RequestEncoding", RequestEncoding, "REQUEST_ENCODING_"] }, - { no: 4, name: "data", kind: "scalar", T: 12 /*ScalarType.BYTES*/ }, - { no: 5, name: "format", kind: "enum", T: () => ["athena.ImageFormat", ImageFormat, "IMAGE_FORMAT_"] }, - { no: 6, name: "hashes", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => ImageHash } - ]); - } - create(value?: PartialMessage): ClassificationInput { - const message = globalThis.Object.create((this.messagePrototype!)); - message.affiliate = ""; - message.correlationId = ""; - message.encoding = 0; - message.data = new Uint8Array(0); - message.format = 0; - message.hashes = []; - if (value !== undefined) - reflectionMergePartial(this, message, value); - return message; - } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ClassificationInput): ClassificationInput { - let message = target ?? this.create(), end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - case /* string affiliate */ 1: - message.affiliate = reader.string(); - break; - case /* string correlation_id */ 2: - message.correlationId = reader.string(); - break; - case /* athena.RequestEncoding encoding */ 3: - message.encoding = reader.int32(); - break; - case /* bytes data */ 4: - message.data = reader.bytes(); - break; - case /* athena.ImageFormat format */ 5: - message.format = reader.int32(); - break; - case /* repeated athena.ImageHash hashes */ 6: - message.hashes.push(ImageHash.internalBinaryRead(reader, reader.uint32(), options)); - break; - default: - let u = options.readUnknownField; - if (u === "throw") - throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); - } - } - return message; - } - internalBinaryWrite(message: ClassificationInput, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { - /* string affiliate = 1; */ - if (message.affiliate !== "") - writer.tag(1, WireType.LengthDelimited).string(message.affiliate); - /* string correlation_id = 2; */ - if (message.correlationId !== "") - writer.tag(2, WireType.LengthDelimited).string(message.correlationId); - /* athena.RequestEncoding encoding = 3; */ - if (message.encoding !== 0) - writer.tag(3, WireType.Varint).int32(message.encoding); - /* bytes data = 4; */ - if (message.data.length) - writer.tag(4, WireType.LengthDelimited).bytes(message.data); - /* athena.ImageFormat format = 5; */ - if (message.format !== 0) - writer.tag(5, WireType.Varint).int32(message.format); - /* repeated athena.ImageHash hashes = 6; */ - for (let i = 0; i < message.hashes.length; i++) - ImageHash.internalBinaryWrite(message.hashes[i], writer.tag(6, WireType.LengthDelimited).fork(), options).join(); - let u = options.writeUnknownFields; - if (u !== false) - (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); - return writer; - } -} -/** - * @generated MessageType for protobuf message athena.ClassificationInput - */ -export const ClassificationInput = new ClassificationInput$Type(); -// @generated message type with reflection information, may provide speed optimized methods -class ClassifyResponse$Type extends MessageType { - constructor() { - super("athena.ClassifyResponse", [ - { no: 1, name: "global_error", kind: "message", T: () => ClassificationError }, - { no: 2, name: "outputs", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => ClassificationOutput } - ]); - } - create(value?: PartialMessage): ClassifyResponse { - const message = globalThis.Object.create((this.messagePrototype!)); - message.outputs = []; - if (value !== undefined) - reflectionMergePartial(this, message, value); - return message; - } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ClassifyResponse): ClassifyResponse { - let message = target ?? this.create(), end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - case /* athena.ClassificationError global_error */ 1: - message.globalError = ClassificationError.internalBinaryRead(reader, reader.uint32(), options, message.globalError); - break; - case /* repeated athena.ClassificationOutput outputs */ 2: - message.outputs.push(ClassificationOutput.internalBinaryRead(reader, reader.uint32(), options)); - break; - default: - let u = options.readUnknownField; - if (u === "throw") - throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); - } - } - return message; - } - internalBinaryWrite(message: ClassifyResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { - /* athena.ClassificationError global_error = 1; */ - if (message.globalError) - ClassificationError.internalBinaryWrite(message.globalError, writer.tag(1, WireType.LengthDelimited).fork(), options).join(); - /* repeated athena.ClassificationOutput outputs = 2; */ - for (let i = 0; i < message.outputs.length; i++) - ClassificationOutput.internalBinaryWrite(message.outputs[i], writer.tag(2, WireType.LengthDelimited).fork(), options).join(); - let u = options.writeUnknownFields; - if (u !== false) - (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); - return writer; - } -} -/** - * @generated MessageType for protobuf message athena.ClassifyResponse - */ -export const ClassifyResponse = new ClassifyResponse$Type(); -// @generated message type with reflection information, may provide speed optimized methods -class ClassificationOutput$Type extends MessageType { - constructor() { - super("athena.ClassificationOutput", [ - { no: 1, name: "correlation_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "classifications", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => Classification }, - { no: 3, name: "error", kind: "message", T: () => ClassificationError } - ]); - } - create(value?: PartialMessage): ClassificationOutput { - const message = globalThis.Object.create((this.messagePrototype!)); - message.correlationId = ""; - message.classifications = []; - if (value !== undefined) - reflectionMergePartial(this, message, value); - return message; - } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ClassificationOutput): ClassificationOutput { - let message = target ?? this.create(), end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - case /* string correlation_id */ 1: - message.correlationId = reader.string(); - break; - case /* repeated athena.Classification classifications */ 2: - message.classifications.push(Classification.internalBinaryRead(reader, reader.uint32(), options)); - break; - case /* athena.ClassificationError error */ 3: - message.error = ClassificationError.internalBinaryRead(reader, reader.uint32(), options, message.error); - break; - default: - let u = options.readUnknownField; - if (u === "throw") - throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); - } - } - return message; - } - internalBinaryWrite(message: ClassificationOutput, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { - /* string correlation_id = 1; */ - if (message.correlationId !== "") - writer.tag(1, WireType.LengthDelimited).string(message.correlationId); - /* repeated athena.Classification classifications = 2; */ - for (let i = 0; i < message.classifications.length; i++) - Classification.internalBinaryWrite(message.classifications[i], writer.tag(2, WireType.LengthDelimited).fork(), options).join(); - /* athena.ClassificationError error = 3; */ - if (message.error) - ClassificationError.internalBinaryWrite(message.error, writer.tag(3, WireType.LengthDelimited).fork(), options).join(); - let u = options.writeUnknownFields; - if (u !== false) - (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); - return writer; - } -} -/** - * @generated MessageType for protobuf message athena.ClassificationOutput - */ -export const ClassificationOutput = new ClassificationOutput$Type(); -// @generated message type with reflection information, may provide speed optimized methods -class Classification$Type extends MessageType { - constructor() { - super("athena.Classification", [ - { no: 1, name: "label", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "weight", kind: "scalar", T: 2 /*ScalarType.FLOAT*/ } - ]); - } - create(value?: PartialMessage): Classification { - const message = globalThis.Object.create((this.messagePrototype!)); - message.label = ""; - message.weight = 0; - if (value !== undefined) - reflectionMergePartial(this, message, value); - return message; - } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: Classification): Classification { - let message = target ?? this.create(), end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - case /* string label */ 1: - message.label = reader.string(); - break; - case /* float weight */ 2: - message.weight = reader.float(); - break; - default: - let u = options.readUnknownField; - if (u === "throw") - throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); - } - } - return message; - } - internalBinaryWrite(message: Classification, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { - /* string label = 1; */ - if (message.label !== "") - writer.tag(1, WireType.LengthDelimited).string(message.label); - /* float weight = 2; */ - if (message.weight !== 0) - writer.tag(2, WireType.Bit32).float(message.weight); - let u = options.writeUnknownFields; - if (u !== false) - (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); - return writer; - } -} -/** - * @generated MessageType for protobuf message athena.Classification - */ -export const Classification = new Classification$Type(); -// @generated message type with reflection information, may provide speed optimized methods -class ClassificationError$Type extends MessageType { - constructor() { - super("athena.ClassificationError", [ - { no: 1, name: "code", kind: "enum", T: () => ["athena.ErrorCode", ErrorCode, "ERROR_CODE_"] }, - { no: 2, name: "message", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 3, name: "details", kind: "scalar", T: 9 /*ScalarType.STRING*/ } - ]); - } - create(value?: PartialMessage): ClassificationError { - const message = globalThis.Object.create((this.messagePrototype!)); - message.code = 0; - message.message = ""; - message.details = ""; - if (value !== undefined) - reflectionMergePartial(this, message, value); - return message; - } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ClassificationError): ClassificationError { - let message = target ?? this.create(), end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - case /* athena.ErrorCode code */ 1: - message.code = reader.int32(); - break; - case /* string message */ 2: - message.message = reader.string(); - break; - case /* string details */ 3: - message.details = reader.string(); - break; - default: - let u = options.readUnknownField; - if (u === "throw") - throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); - } - } - return message; - } - internalBinaryWrite(message: ClassificationError, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { - /* athena.ErrorCode code = 1; */ - if (message.code !== 0) - writer.tag(1, WireType.Varint).int32(message.code); - /* string message = 2; */ - if (message.message !== "") - writer.tag(2, WireType.LengthDelimited).string(message.message); - /* string details = 3; */ - if (message.details !== "") - writer.tag(3, WireType.LengthDelimited).string(message.details); - let u = options.writeUnknownFields; - if (u !== false) - (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); - return writer; - } -} -/** - * @generated MessageType for protobuf message athena.ClassificationError - */ -export const ClassificationError = new ClassificationError$Type(); -// @generated message type with reflection information, may provide speed optimized methods -class ImageHash$Type extends MessageType { - constructor() { - super("athena.ImageHash", [ - { no: 1, name: "value", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "type", kind: "enum", T: () => ["athena.HashType", HashType, "HASH_TYPE_"] } - ]); - } - create(value?: PartialMessage): ImageHash { - const message = globalThis.Object.create((this.messagePrototype!)); - message.value = ""; - message.type = 0; - if (value !== undefined) - reflectionMergePartial(this, message, value); - return message; - } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ImageHash): ImageHash { - let message = target ?? this.create(), end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - case /* string value */ 1: - message.value = reader.string(); - break; - case /* athena.HashType type */ 2: - message.type = reader.int32(); - break; - default: - let u = options.readUnknownField; - if (u === "throw") - throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); - } - } - return message; - } - internalBinaryWrite(message: ImageHash, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { - /* string value = 1; */ - if (message.value !== "") - writer.tag(1, WireType.LengthDelimited).string(message.value); - /* athena.HashType type = 2; */ - if (message.type !== 0) - writer.tag(2, WireType.Varint).int32(message.type); - let u = options.writeUnknownFields; - if (u !== false) - (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); - return writer; - } -} -/** - * @generated MessageType for protobuf message athena.ImageHash - */ -export const ImageHash = new ImageHash$Type(); diff --git a/src/generated/google/protobuf/empty.ts b/src/generated/google/protobuf/empty.ts deleted file mode 100644 index f4271a3..0000000 --- a/src/generated/google/protobuf/empty.ts +++ /dev/null @@ -1,94 +0,0 @@ -// @generated by protobuf-ts 2.11.1 with parameter client_grpc1 -// @generated from protobuf file "google/protobuf/empty.proto" (package "google.protobuf", syntax proto3) -// tslint:disable -// -// Protocol Buffers - Google's data interchange format -// Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -import type { BinaryWriteOptions } from "@protobuf-ts/runtime"; -import type { IBinaryWriter } from "@protobuf-ts/runtime"; -import type { BinaryReadOptions } from "@protobuf-ts/runtime"; -import type { IBinaryReader } from "@protobuf-ts/runtime"; -import { UnknownFieldHandler } from "@protobuf-ts/runtime"; -import type { PartialMessage } from "@protobuf-ts/runtime"; -import { reflectionMergePartial } from "@protobuf-ts/runtime"; -import { MessageType } from "@protobuf-ts/runtime"; -/** - * A generic empty message that you can re-use to avoid defining duplicated - * empty messages in your APIs. A typical example is to use it as the request - * or the response type of an API method. For instance: - * - * service Foo { - * rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - * } - * - * - * @generated from protobuf message google.protobuf.Empty - */ -export interface Empty { -} -// @generated message type with reflection information, may provide speed optimized methods -class Empty$Type extends MessageType { - constructor() { - super("google.protobuf.Empty", []); - } - create(value?: PartialMessage): Empty { - const message = globalThis.Object.create((this.messagePrototype!)); - if (value !== undefined) - reflectionMergePartial(this, message, value); - return message; - } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: Empty): Empty { - let message = target ?? this.create(), end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - default: - let u = options.readUnknownField; - if (u === "throw") - throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); - } - } - return message; - } - internalBinaryWrite(message: Empty, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { - let u = options.writeUnknownFields; - if (u !== false) - (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); - return writer; - } -} -/** - * @generated MessageType for protobuf message google.protobuf.Empty - */ -export const Empty = new Empty$Type(); From e460c03cde2d9a02f94f2c0cf6762624fb24c7f2 Mon Sep 17 00:00:00 2001 From: snus-kin Date: Wed, 10 Sep 2025 16:12:45 +0100 Subject: [PATCH 3/8] fix: init submodules in CI --- .github/workflows/functional-tests.yml | 3 +++ .github/workflows/nodejs.yml | 3 +++ .github/workflows/release.yml | 8 +++++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/functional-tests.yml b/.github/workflows/functional-tests.yml index 2bb2c71..e17f1c7 100644 --- a/.github/workflows/functional-tests.yml +++ b/.github/workflows/functional-tests.yml @@ -29,6 +29,9 @@ jobs: - name: Checkout code uses: actions/checkout@v5 + - name: Submodules + run: git submodule update --init --recursive + - name: Setup Node.js uses: volta-cli/action@v4 diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 5e924ac..95b515b 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -13,6 +13,9 @@ jobs: - name: Checkout code uses: actions/checkout@v5 + - name: Submodules + run: git submodule update --init --recursive + - name: Setup Node.js uses: volta-cli/action@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4f8e329..ee2a831 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,8 +11,14 @@ jobs: - uses: actions/setup-node@v5 with: node-version: '22' - - run: npm ci + + - name: Submodules + run: git submodule update --init --recursive + - run: npm run codegen + + - run: npm ci + - run: npm run build:release - uses: JS-DevTools/npm-publish@v3 with: From 5ef9bf4f2dffb993b2acbdccbe797647402c8db0 Mon Sep 17 00:00:00 2001 From: snus-kin Date: Thu, 11 Sep 2025 15:55:55 +0100 Subject: [PATCH 4/8] ci: update version in release pipeline --- .github/workflows/release.yml | 16 +++++++++++++++- package.json | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ee2a831..60c742d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,6 @@ -on: workflow_dispatch +on: + release: + types: [published] jobs: publish: @@ -15,6 +17,18 @@ jobs: - name: Submodules run: git submodule update --init --recursive + - name: Set version from tag + run: | + if [[ "$GITHUB_REF" == refs/tags/* ]]; then + TAG_VERSION=${GITHUB_REF#refs/tags/} + # Remove 'v' prefix if present + VERSION=${TAG_VERSION#v} + echo "Setting version to: $VERSION" + npm version "$VERSION" + else + echo "Not building from a tag, using default version" + fi + - run: npm run codegen - run: npm ci diff --git a/package.json b/package.json index baf405c..5ab65b3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@crispthinking/athena-classifier-sdk", - "version": "0.2.0", + "version": "0.0.0", "main": "./dist/src/index.js", "description": "A Node.js SDK for the Athena classifier that uses gRPC transport.", "type": "module", From be1e8e1a4a6b93107f7f1b3cb31fedb5a000d07b Mon Sep 17 00:00:00 2001 From: snus-kin Date: Fri, 12 Sep 2025 10:33:17 +0100 Subject: [PATCH 5/8] feat: classify single method --- src/hashing.ts | 2 +- src/index.ts | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/hashing.ts b/src/hashing.ts index 8a4c8c6..77e6964 100644 --- a/src/hashing.ts +++ b/src/hashing.ts @@ -68,7 +68,7 @@ export async function computeHashesFromStream( } if (encoding === RequestEncoding.BROTLI) { - data = Buffer.from(await brotli.compress(data)); + data = Buffer.from(brotli.compress(data)); } return { diff --git a/src/index.ts b/src/index.ts index cda3b34..1e7dae3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,7 @@ import { ClassifyResponse, Deployment, ImageFormat, + ClassificationOutput, } from './generated/athena/models'; import * as grpc from '@grpc/grpc-js'; import { @@ -308,6 +309,60 @@ export class ClassifierSdk extends (EventEmitter as new () => TypedEventEmitter< }); } + public async classifySingle( + request: ClassifyImageInput, + ): Promise { + const options = { + affiliate: this.options.affiliate, + correlationId: randomUUID().toString(), + includeHashes: [HashType.MD5, HashType.SHA1], + encoding: RequestEncoding.UNCOMPRESSED, + }; + + let inputFormat: ImageFormat = ImageFormat.UNSPECIFIED; + + if ('resize' in request === false) { + inputFormat = request.format; + } + + const { md5, sha1, data, format } = await computeHashesFromStream( + request.data, + options.encoding, + inputFormat, + 'resize' in options, + options.includeHashes, + ); + + const hashes: ImageHash[] = []; + + if (md5 && md5.trim() != '') { + hashes.push({ value: md5, type: HashType.MD5 }); + } + + if (sha1 && sha1.trim() != '') { + hashes.push({ value: sha1, type: HashType.SHA1 }); + } + + const input: ClassificationInput = { + affiliate: this.options.affiliate, + correlationId: options.correlationId, + data: data, + format: format, + encoding: RequestEncoding.UNCOMPRESSED, + hashes: hashes, + }; + + return new Promise((resolve, reject) => { + this.client.classifySingle(input, (err, response) => { + if (err) { + reject(err); + } else { + resolve(response); + } + }); + }); + } + /** * Closes the gRPC stream and cleans up resources. * Emits 'close' event. From 3f9d23b489993bfd143521db5ea6e338b50d3f2b Mon Sep 17 00:00:00 2001 From: snus-kin Date: Fri, 12 Sep 2025 11:30:34 +0100 Subject: [PATCH 6/8] test: add tests for classify single --- __tests__/functional/main.functional.test.ts | 151 +++++++++++++------ __tests__/unit/main.test.ts | 4 + 2 files changed, 108 insertions(+), 47 deletions(-) diff --git a/__tests__/functional/main.functional.test.ts b/__tests__/functional/main.functional.test.ts index 0a46ede..106e0b1 100644 --- a/__tests__/functional/main.functional.test.ts +++ b/__tests__/functional/main.functional.test.ts @@ -1,11 +1,18 @@ -import { describe, it, } from 'vitest'; -import { ClassificationOutput, ClassifierSdk, type ClassifyImageInput, ImageFormat } from '../../src'; +import { describe, it } from 'vitest'; +import { + ClassificationOutput, + ClassifierSdk, + type ClassifyImageInput, + ImageFormat, +} from '../../src'; import fs from 'fs'; import { randomUUID } from 'crypto'; describe('ClassifierSdk Functional Tests', () => { describe('listDeployments', () => { - it('should listDeployments and return responses (smoke test)', async ({ expect }) => { + it('should listDeployments and return responses (smoke test)', async ({ + expect, + }) => { // This is a smoke test. You must have a running gRPC server at localhost:50051 for this to pass. // You may want to mock the gRPC client for true unit testing. const sdk = new ClassifierSdk({ @@ -15,8 +22,8 @@ describe('ClassifierSdk Functional Tests', () => { issuerUrl: process.env.VITE_OAUTH_ISSUER, clientId: process.env.VITE_ATHENA_CLIENT_ID, clientSecret: process.env.VITE_ATHENA_CLIENT_SECRET, - scope: 'manage:classify' - } + scope: 'manage:classify', + }, }); let error: any = null; @@ -28,11 +35,41 @@ describe('ClassifierSdk Functional Tests', () => { } // Assert error is unset expect(error).toBeNull(); - }, 10000) + }, 10000); + }); + + describe('classifySingle', () => { + it('should classify a single image and return the response', async ({ + expect, + }) => { + const imagePath = __dirname + '/448x448.jpg'; + const sdk = new ClassifierSdk({ + deploymentId: process.env.VITE_ATHENA_DEPLOYMENT_ID, + affiliate: process.env.VITE_ATHENA_AFFILIATE, + authentication: { + issuerUrl: process.env.VITE_OAUTH_ISSUER, + clientId: process.env.VITE_ATHENA_CLIENT_ID, + clientSecret: process.env.VITE_ATHENA_CLIENT_SECRET, + scope: 'manage:classify', + }, + }); + + const input: ClassifyImageInput = { + data: fs.createReadStream(imagePath), + format: ImageFormat.PNG, + }; + + const response = await sdk.classifySingle(input); + expect(response.classifications).toBe(true); + expect(response.error).toBeNull(); + }, 10000); }); describe('classifyImage', () => { - it('should classify 10 images in a single request and return responses (integration smoke test)', async ({ expect, annotate }) => { + it('should classify 10 images in a single request and return responses (integration smoke test)', async ({ + expect, + annotate, + }) => { // This is a smoke test. You must have a running gRPC server at localhost:50051 for this to pass. // You may want to mock the gRPC client for true unit testing. const imagePath = __dirname + '/448x448.jpg'; @@ -43,23 +80,27 @@ describe('ClassifierSdk Functional Tests', () => { issuerUrl: process.env.VITE_OAUTH_ISSUER, clientId: process.env.VITE_ATHENA_CLIENT_ID, clientSecret: process.env.VITE_ATHENA_CLIENT_SECRET, - scope: 'manage:classify' - } + scope: 'manage:classify', + }, }); // Generate 10 unique correlationIds - const correlationIds = Array.from({ length: 10 }, () => randomUUID().toString()); + const correlationIds = Array.from({ length: 10 }, () => + randomUUID().toString(), + ); correlationIds.sort((a, b) => a.localeCompare(b)); annotate(`Correlation IDs: ${correlationIds.join(', ')}`); // Create 10 input objects, each with a new stream and unique correlationId - const inputs: ClassifyImageInput[] = correlationIds.map((correlationId) => ({ - data: fs.createReadStream(imagePath), - format: ImageFormat.PNG, - correlationId - })); + const inputs: ClassifyImageInput[] = correlationIds.map( + (correlationId) => ({ + data: fs.createReadStream(imagePath), + format: ImageFormat.PNG, + correlationId, + }), + ); // Create a promise to wrap the event emitter event 'data' const promise = new Promise((resolve, reject) => { @@ -107,23 +148,26 @@ describe('ClassifierSdk Functional Tests', () => { // Check that all correlationIds are present in the outputs expect(outputs.length).toBe(correlationIds.length); - const expectedOutputs = correlationIds.map(id => ( - { - correlationId: id, - classifications: expect.toBeOneOf([expect.arrayContaining([ + const expectedOutputs = correlationIds.map((id) => ({ + correlationId: id, + classifications: expect.toBeOneOf([ + expect.arrayContaining([ { label: expect.any(String), - weight: expect.any(Number) - } - ]), []]) - } - )); + weight: expect.any(Number), + }, + ]), + [], + ]), + })); expect(outputs).toMatchObject(expectedOutputs); }, 120000); - it('should classify with raw uint8 resize return responses (integration smoke test)', async ({ expect, annotate }) => { - + it('should classify with raw uint8 resize return responses (integration smoke test)', async ({ + expect, + annotate, + }) => { const imagePath = __dirname + '/448x448.jpg'; const sdk = new ClassifierSdk({ deploymentId: process.env.VITE_ATHENA_DEPLOYMENT_ID, @@ -132,8 +176,8 @@ describe('ClassifierSdk Functional Tests', () => { issuerUrl: process.env.VITE_OAUTH_ISSUER, clientId: process.env.VITE_ATHENA_CLIENT_ID, clientSecret: process.env.VITE_ATHENA_CLIENT_SECRET, - scope: 'manage:classify' - } + scope: 'manage:classify', + }, }); const correlationId = randomUUID(); @@ -148,7 +192,9 @@ describe('ClassifierSdk Functional Tests', () => { }, 30000); sdk.on('data', (data) => { - const byCorrelationId = data.outputs.filter(o => o.correlationId === correlationId); + const byCorrelationId = data.outputs.filter( + (o) => o.correlationId === correlationId, + ); if (byCorrelationId.length > 0) { clearTimeout(timeout); resolve(byCorrelationId); @@ -185,20 +231,26 @@ describe('ClassifierSdk Functional Tests', () => { expect(first).toMatchObject([ { correlationId, - classifications: expect.toBeOneOf([expect.arrayContaining([ - { - label: expect.any(String), - weight: expect.any(Number) - } - ]), []]) - } as ClassificationOutput + classifications: expect.toBeOneOf([ + expect.arrayContaining([ + { + label: expect.any(String), + weight: expect.any(Number), + }, + ]), + [], + ]), + } as ClassificationOutput, ]); // Accept either a successful call or a connection error (for CI/dev convenience) expect(error).toBeUndefined(); }, 120000); - it('should classify return responses (integration smoke test)', async ({ expect, annotate }) => { + it('should classify return responses (integration smoke test)', async ({ + expect, + annotate, + }) => { // This is a smoke test. You must have a running gRPC server at localhost:50051 for this to pass. // You may want to mock the gRPC client for true unit testing. const imagePath = __dirname + '/448x448.jpg'; @@ -209,8 +261,8 @@ describe('ClassifierSdk Functional Tests', () => { issuerUrl: process.env.VITE_OAUTH_ISSUER, clientId: process.env.VITE_ATHENA_CLIENT_ID, clientSecret: process.env.VITE_ATHENA_CLIENT_SECRET, - scope: 'manage:classify' - } + scope: 'manage:classify', + }, }); const correlationId = randomUUID(); @@ -225,7 +277,9 @@ describe('ClassifierSdk Functional Tests', () => { }, 30000); sdk.on('data', (data) => { - const byCorrelationId = data.outputs.filter(o => o.correlationId === correlationId); + const byCorrelationId = data.outputs.filter( + (o) => o.correlationId === correlationId, + ); if (byCorrelationId.length > 0) { clearTimeout(timeout); resolve(byCorrelationId); @@ -262,13 +316,16 @@ describe('ClassifierSdk Functional Tests', () => { expect(first).toMatchObject([ { correlationId, - classifications: expect.toBeOneOf([expect.arrayContaining([ - { - label: expect.any(String), - weight: expect.any(Number) - } - ]), []]) - } as ClassificationOutput + classifications: expect.toBeOneOf([ + expect.arrayContaining([ + { + label: expect.any(String), + weight: expect.any(Number), + }, + ]), + [], + ]), + } as ClassificationOutput, ]); // Accept either a successful call or a connection error (for CI/dev convenience) diff --git a/__tests__/unit/main.test.ts b/__tests__/unit/main.test.ts index ba4dc16..b065d1f 100644 --- a/__tests__/unit/main.test.ts +++ b/__tests__/unit/main.test.ts @@ -122,6 +122,10 @@ describe('ClassifierSdk', () => { expect(typeof sdk.sendClassifyRequest).toBe('function'); }); + it('should have classifySingle method', () => { + expect(typeof sdk.classifySingle).toBe('function'); + }); + it('should throw error when sendClassifyRequest called without open', async () => { const input = { data: Buffer.from('test'), From 07b4b1c12becc2c566252205279cdeb8b665d7c9 Mon Sep 17 00:00:00 2001 From: snus-kin Date: Fri, 12 Sep 2025 11:32:57 +0100 Subject: [PATCH 7/8] fix: request encoding --- src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 1e7dae3..6814126 100644 --- a/src/index.ts +++ b/src/index.ts @@ -316,7 +316,7 @@ export class ClassifierSdk extends (EventEmitter as new () => TypedEventEmitter< affiliate: this.options.affiliate, correlationId: randomUUID().toString(), includeHashes: [HashType.MD5, HashType.SHA1], - encoding: RequestEncoding.UNCOMPRESSED, + encoding: request.encoding, }; let inputFormat: ImageFormat = ImageFormat.UNSPECIFIED; @@ -348,7 +348,7 @@ export class ClassifierSdk extends (EventEmitter as new () => TypedEventEmitter< correlationId: options.correlationId, data: data, format: format, - encoding: RequestEncoding.UNCOMPRESSED, + encoding: options.encoding, hashes: hashes, }; From bf3f1d4028bfa85cfb7f8ccffa1af5c4eb652e1c Mon Sep 17 00:00:00 2001 From: snus-kin Date: Fri, 12 Sep 2025 15:43:28 +0100 Subject: [PATCH 8/8] fix: check for resize in request --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 6814126..428db2b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -329,7 +329,7 @@ export class ClassifierSdk extends (EventEmitter as new () => TypedEventEmitter< request.data, options.encoding, inputFormat, - 'resize' in options, + 'resize' in request, options.includeHashes, );