Merge pull request 'feat: add gRPC-Web client service layer for OrchestratorService' (#24) from feature/issue-4-grpc-web-client into main
This commit was merged in pull request #24.
This commit is contained in:
@@ -5,3 +5,4 @@
|
|||||||
| #1 | Project scaffolding: SvelteKit + Tailwind + TypeScript | COMPLETED | [issue-001.md](issue-001.md) |
|
| #1 | Project scaffolding: SvelteKit + Tailwind + TypeScript | COMPLETED | [issue-001.md](issue-001.md) |
|
||||||
| #2 | Proto codegen pipeline for TypeScript gRPC-Web stubs | COMPLETED | [issue-002.md](issue-002.md) |
|
| #2 | Proto codegen pipeline for TypeScript gRPC-Web stubs | COMPLETED | [issue-002.md](issue-002.md) |
|
||||||
| #3 | Configure Caddy for gRPC-Web support | COMPLETED | [issue-003.md](issue-003.md) |
|
| #3 | Configure Caddy for gRPC-Web support | COMPLETED | [issue-003.md](issue-003.md) |
|
||||||
|
| #4 | gRPC-Web client service layer | COMPLETED | [issue-004.md](issue-004.md) |
|
||||||
|
|||||||
25
implementation-plans/issue-004.md
Normal file
25
implementation-plans/issue-004.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
---
|
||||||
|
|
||||||
|
# Issue #4: gRPC-Web client service layer
|
||||||
|
|
||||||
|
**Status:** COMPLETED
|
||||||
|
**Issue:** https://git.shahondin1624.de/llm-multiverse/llm-multiverse-ui/issues/4
|
||||||
|
**Branch:** `feature/issue-4-grpc-web-client`
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Create `src/lib/services/orchestrator.ts` wrapping Connect-Web client for the OrchestratorService, with typed `processRequest()` async generator and error mapping.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [x] `src/lib/services/orchestrator.ts` module created
|
||||||
|
- [x] Wraps generated gRPC-Web stubs with typed interface
|
||||||
|
- [x] Connection setup with configurable endpoint
|
||||||
|
- [x] Error mapping from gRPC status codes to application errors
|
||||||
|
- [x] `processRequest()` returns an async iterator of `ProcessRequestResponse`
|
||||||
|
- [x] Handles streaming responses correctly
|
||||||
|
|
||||||
|
## Deviations
|
||||||
|
|
||||||
|
None.
|
||||||
89
src/lib/services/orchestrator.ts
Normal file
89
src/lib/services/orchestrator.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { createClient } from '@connectrpc/connect';
|
||||||
|
import { createGrpcWebTransport } from '@connectrpc/connect-web';
|
||||||
|
import { OrchestratorService } from '$lib/proto/llm_multiverse/v1/orchestrator_pb';
|
||||||
|
import type {
|
||||||
|
ProcessRequestResponse,
|
||||||
|
SessionConfig
|
||||||
|
} from '$lib/proto/llm_multiverse/v1/orchestrator_pb';
|
||||||
|
import { create } from '@bufbuild/protobuf';
|
||||||
|
import { ProcessRequestRequestSchema } from '$lib/proto/llm_multiverse/v1/orchestrator_pb';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Application-level error wrapping gRPC status codes.
|
||||||
|
*/
|
||||||
|
export class OrchestratorError extends Error {
|
||||||
|
constructor(
|
||||||
|
message: string,
|
||||||
|
public readonly code: string,
|
||||||
|
public readonly details?: string
|
||||||
|
) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'OrchestratorError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_ENDPOINT = '/';
|
||||||
|
|
||||||
|
let transport: ReturnType<typeof createGrpcWebTransport> | null = null;
|
||||||
|
|
||||||
|
function getTransport(endpoint?: string) {
|
||||||
|
if (!transport) {
|
||||||
|
transport = createGrpcWebTransport({
|
||||||
|
baseUrl: endpoint ?? DEFAULT_ENDPOINT
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the transport (useful for reconfiguring the endpoint).
|
||||||
|
*/
|
||||||
|
export function resetTransport(): void {
|
||||||
|
transport = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a configured orchestrator client.
|
||||||
|
*/
|
||||||
|
function getClient(endpoint?: string) {
|
||||||
|
return createClient(OrchestratorService, getTransport(endpoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a request to the orchestrator and yield streaming responses.
|
||||||
|
*
|
||||||
|
* Returns an async iterator of `ProcessRequestResponse` messages,
|
||||||
|
* each containing the current orchestration state, status message,
|
||||||
|
* and optionally intermediate or final results.
|
||||||
|
*/
|
||||||
|
export async function* processRequest(
|
||||||
|
sessionId: string,
|
||||||
|
userMessage: string,
|
||||||
|
sessionConfig?: SessionConfig,
|
||||||
|
endpoint?: string
|
||||||
|
): AsyncGenerator<ProcessRequestResponse> {
|
||||||
|
const client = getClient(endpoint);
|
||||||
|
|
||||||
|
const request = create(ProcessRequestRequestSchema, {
|
||||||
|
sessionId,
|
||||||
|
userMessage,
|
||||||
|
sessionConfig
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
for await (const response of client.processRequest(request)) {
|
||||||
|
yield response;
|
||||||
|
}
|
||||||
|
} catch (err: unknown) {
|
||||||
|
if (err instanceof Error) {
|
||||||
|
// ConnectError has a `code` property
|
||||||
|
const code = 'code' in err ? (err as { code: unknown }).code : undefined;
|
||||||
|
throw new OrchestratorError(
|
||||||
|
err.message,
|
||||||
|
typeof code === 'string' ? code : 'unknown',
|
||||||
|
err.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw new OrchestratorError('Unknown error', 'unknown');
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user