From a7112eab99d8b0275ef56a42158596fa3ef9125e Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Wed, 10 Jun 2026 00:16:07 +0900 Subject: [PATCH] feat(agent): design @typia/agent streaming function-calling harness interface Introduces the interface-only (types + docs) design for `@typia/agent`, a vendor-neutral streaming function-calling harness built on typia's validation. - `IAgent`/`TypiaAgent`: `conversate()` returns an `IAgentExecution` that is an async-iterable of `IAgentResponse` parts (`text | tool`) consumed with `for await`, and returns an `IAgentTurn` outcome. - Harness surface: incremental validation (`IAgentValidation` snapshot/locked/ watch/phase), the `IAgentTool` parse->validate->feedback->execute state machine, and output-token-ceiling continuation. - `IAgentAdapter`: vendor-neutral chat-stream seam (OpenAI/Vercel), with the unified `ILlmSchema` keeping all provider knowledge inside the adapter. - Controllers/operations (`class | http | mcp | output`), `IAgentSelector` for large function counts, serializable `IAgentHistory(.Json)` memory, and a secondary `IAgentEvent` telemetry channel. - One type per file (no namespace nesting); `ARCHITECTURE.md` documents the rationale; `tsgo --noEmit` is clean. Types only; no runtime implementation yet (adapters + harness loop follow). Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitignore | 1 + packages/agent/ARCHITECTURE.md | 161 ++++++++++++++++++ packages/agent/INSTRUCTION.md | 36 ++++ packages/agent/package.json | 77 +++++++++ packages/agent/rollup.config.mjs | 1 + packages/agent/src/IAgent.ts | 71 ++++++++ packages/agent/src/TypiaAgent.ts | 80 +++++++++ .../src/events/IAgentContinuationEvent.ts | 14 ++ packages/agent/src/events/IAgentEvent.ts | 27 +++ packages/agent/src/events/IAgentEventBase.ts | 16 ++ .../agent/src/events/IAgentEventMapper.ts | 20 +++ .../agent/src/events/IAgentEventSource.ts | 6 + packages/agent/src/events/IAgentParseEvent.ts | 14 ++ .../agent/src/events/IAgentRequestEvent.ts | 13 ++ .../agent/src/events/IAgentResponseEvent.ts | 14 ++ packages/agent/src/events/IAgentUsageEvent.ts | 11 ++ .../agent/src/events/IAgentValidateEvent.ts | 16 ++ packages/agent/src/histories/IAgentHistory.ts | 19 +++ .../agent/src/histories/IAgentHistoryBase.ts | 16 ++ .../agent/src/histories/IAgentHistoryJson.ts | 14 ++ .../src/histories/IAgentSystemHistory.ts | 11 ++ .../src/histories/IAgentSystemHistoryJson.ts | 10 ++ .../agent/src/histories/IAgentTextHistory.ts | 18 ++ .../src/histories/IAgentTextHistoryJson.ts | 13 ++ .../agent/src/histories/IAgentToolHistory.ts | 21 +++ .../src/histories/IAgentToolHistoryJson.ts | 28 +++ packages/agent/src/index.ts | 83 +++++++++ packages/agent/src/responses/IAgentExecute.ts | 34 ++++ .../agent/src/responses/IAgentExecution.ts | 50 ++++++ .../src/responses/IAgentFeedbackProps.ts | 18 ++ .../agent/src/responses/IAgentResponse.ts | 24 +++ packages/agent/src/responses/IAgentText.ts | 49 ++++++ packages/agent/src/responses/IAgentTool.ts | 82 +++++++++ packages/agent/src/responses/IAgentTurn.ts | 34 ++++ .../agent/src/responses/IAgentValidation.ts | 54 ++++++ .../src/responses/IAgentValidationState.ts | 32 ++++ .../agent/src/structures/IAgentAdapter.ts | 46 +++++ .../structures/IAgentAdapterCapabilities.ts | 18 ++ .../src/structures/IAgentAdapterRequest.ts | 31 ++++ .../src/structures/IAgentAudioContent.ts | 15 ++ packages/agent/src/structures/IAgentChunk.ts | 18 ++ .../src/structures/IAgentClassController.ts | 30 ++++ .../src/structures/IAgentClassExecuteProps.ts | 18 ++ .../src/structures/IAgentClassOperation.ts | 11 ++ .../agent/src/structures/IAgentCompaction.ts | 13 ++ packages/agent/src/structures/IAgentConfig.ts | 71 ++++++++ .../src/structures/IAgentContinuation.ts | 26 +++ .../agent/src/structures/IAgentController.ts | 22 +++ .../src/structures/IAgentConversateOptions.ts | 9 + .../agent/src/structures/IAgentErrorChunk.ts | 12 ++ .../agent/src/structures/IAgentFileContent.ts | 21 +++ .../agent/src/structures/IAgentFinishChunk.ts | 18 ++ .../src/structures/IAgentFinishReason.ts | 15 ++ .../src/structures/IAgentHttpController.ts | 28 +++ .../src/structures/IAgentHttpExecuteProps.ts | 20 +++ .../src/structures/IAgentHttpOperation.ts | 16 ++ .../src/structures/IAgentImageContent.ts | 15 ++ .../src/structures/IAgentMcpController.ts | 24 +++ .../src/structures/IAgentMcpOperation.ts | 11 ++ .../agent/src/structures/IAgentMessage.ts | 22 +++ .../src/structures/IAgentMessageContent.ts | 20 +++ .../agent/src/structures/IAgentOperation.ts | 24 +++ .../src/structures/IAgentOperationBase.ts | 25 +++ .../src/structures/IAgentOutputOperation.ts | 12 ++ packages/agent/src/structures/IAgentProps.ts | 39 +++++ .../agent/src/structures/IAgentRawChunk.ts | 14 ++ .../agent/src/structures/IAgentSelector.ts | 40 +++++ .../src/structures/IAgentSelectorProps.ts | 21 +++ .../src/structures/IAgentSystemPrompt.ts | 15 ++ .../agent/src/structures/IAgentTextContent.ts | 12 ++ .../src/structures/IAgentTextDeltaChunk.ts | 12 ++ .../agent/src/structures/IAgentTokenUsage.ts | 31 ++++ .../src/structures/IAgentTokenUsageInput.ts | 12 ++ .../src/structures/IAgentTokenUsageOutput.ts | 12 ++ packages/agent/tsconfig.json | 8 + pnpm-lock.yaml | 40 +++++ 76 files changed, 2055 insertions(+) create mode 100644 packages/agent/ARCHITECTURE.md create mode 100644 packages/agent/INSTRUCTION.md create mode 100644 packages/agent/package.json create mode 100644 packages/agent/rollup.config.mjs create mode 100644 packages/agent/src/IAgent.ts create mode 100644 packages/agent/src/TypiaAgent.ts create mode 100644 packages/agent/src/events/IAgentContinuationEvent.ts create mode 100644 packages/agent/src/events/IAgentEvent.ts create mode 100644 packages/agent/src/events/IAgentEventBase.ts create mode 100644 packages/agent/src/events/IAgentEventMapper.ts create mode 100644 packages/agent/src/events/IAgentEventSource.ts create mode 100644 packages/agent/src/events/IAgentParseEvent.ts create mode 100644 packages/agent/src/events/IAgentRequestEvent.ts create mode 100644 packages/agent/src/events/IAgentResponseEvent.ts create mode 100644 packages/agent/src/events/IAgentUsageEvent.ts create mode 100644 packages/agent/src/events/IAgentValidateEvent.ts create mode 100644 packages/agent/src/histories/IAgentHistory.ts create mode 100644 packages/agent/src/histories/IAgentHistoryBase.ts create mode 100644 packages/agent/src/histories/IAgentHistoryJson.ts create mode 100644 packages/agent/src/histories/IAgentSystemHistory.ts create mode 100644 packages/agent/src/histories/IAgentSystemHistoryJson.ts create mode 100644 packages/agent/src/histories/IAgentTextHistory.ts create mode 100644 packages/agent/src/histories/IAgentTextHistoryJson.ts create mode 100644 packages/agent/src/histories/IAgentToolHistory.ts create mode 100644 packages/agent/src/histories/IAgentToolHistoryJson.ts create mode 100644 packages/agent/src/index.ts create mode 100644 packages/agent/src/responses/IAgentExecute.ts create mode 100644 packages/agent/src/responses/IAgentExecution.ts create mode 100644 packages/agent/src/responses/IAgentFeedbackProps.ts create mode 100644 packages/agent/src/responses/IAgentResponse.ts create mode 100644 packages/agent/src/responses/IAgentText.ts create mode 100644 packages/agent/src/responses/IAgentTool.ts create mode 100644 packages/agent/src/responses/IAgentTurn.ts create mode 100644 packages/agent/src/responses/IAgentValidation.ts create mode 100644 packages/agent/src/responses/IAgentValidationState.ts create mode 100644 packages/agent/src/structures/IAgentAdapter.ts create mode 100644 packages/agent/src/structures/IAgentAdapterCapabilities.ts create mode 100644 packages/agent/src/structures/IAgentAdapterRequest.ts create mode 100644 packages/agent/src/structures/IAgentAudioContent.ts create mode 100644 packages/agent/src/structures/IAgentChunk.ts create mode 100644 packages/agent/src/structures/IAgentClassController.ts create mode 100644 packages/agent/src/structures/IAgentClassExecuteProps.ts create mode 100644 packages/agent/src/structures/IAgentClassOperation.ts create mode 100644 packages/agent/src/structures/IAgentCompaction.ts create mode 100644 packages/agent/src/structures/IAgentConfig.ts create mode 100644 packages/agent/src/structures/IAgentContinuation.ts create mode 100644 packages/agent/src/structures/IAgentController.ts create mode 100644 packages/agent/src/structures/IAgentConversateOptions.ts create mode 100644 packages/agent/src/structures/IAgentErrorChunk.ts create mode 100644 packages/agent/src/structures/IAgentFileContent.ts create mode 100644 packages/agent/src/structures/IAgentFinishChunk.ts create mode 100644 packages/agent/src/structures/IAgentFinishReason.ts create mode 100644 packages/agent/src/structures/IAgentHttpController.ts create mode 100644 packages/agent/src/structures/IAgentHttpExecuteProps.ts create mode 100644 packages/agent/src/structures/IAgentHttpOperation.ts create mode 100644 packages/agent/src/structures/IAgentImageContent.ts create mode 100644 packages/agent/src/structures/IAgentMcpController.ts create mode 100644 packages/agent/src/structures/IAgentMcpOperation.ts create mode 100644 packages/agent/src/structures/IAgentMessage.ts create mode 100644 packages/agent/src/structures/IAgentMessageContent.ts create mode 100644 packages/agent/src/structures/IAgentOperation.ts create mode 100644 packages/agent/src/structures/IAgentOperationBase.ts create mode 100644 packages/agent/src/structures/IAgentOutputOperation.ts create mode 100644 packages/agent/src/structures/IAgentProps.ts create mode 100644 packages/agent/src/structures/IAgentRawChunk.ts create mode 100644 packages/agent/src/structures/IAgentSelector.ts create mode 100644 packages/agent/src/structures/IAgentSelectorProps.ts create mode 100644 packages/agent/src/structures/IAgentSystemPrompt.ts create mode 100644 packages/agent/src/structures/IAgentTextContent.ts create mode 100644 packages/agent/src/structures/IAgentTextDeltaChunk.ts create mode 100644 packages/agent/src/structures/IAgentTokenUsage.ts create mode 100644 packages/agent/src/structures/IAgentTokenUsageInput.ts create mode 100644 packages/agent/src/structures/IAgentTokenUsageOutput.ts create mode 100644 packages/agent/tsconfig.json diff --git a/.gitignore b/.gitignore index ca5bb0766cc..9f278e7adf9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .codex/* !.codex/skills/ .discussions/ +.repositories/ .wiki/ bin/ diff --git a/packages/agent/ARCHITECTURE.md b/packages/agent/ARCHITECTURE.md new file mode 100644 index 00000000000..70485374cf8 --- /dev/null +++ b/packages/agent/ARCHITECTURE.md @@ -0,0 +1,161 @@ +# `@typia/agent` — 아키텍처 + +> 상태: **인터페이스 설계(타입 전용)**. 이 문서와 `src/*.ts` 타입 정의가 1차 산출물이다. 런타임 구현은 아직 출하되지 않았다. 모든 결정의 근거가 되는 연구 흔적은 repo 루트의 git-ignore된 `.wiki/`에 있다. + +## 1. 이것이 무엇인가 + +`@typia/agent`는 typia의 컴파일 타임 validation 위에 구축된, 벤더 중립적인 **streaming function-calling harness**다. `MicroAgentica` 스타일의 대화형 agent를 제공하되, 기존의 모든 프레임워크와 세 축에서 다르다. + +1. **max-output-token 천장을 넘는다.** 모델이 structured data를 *streamed text*로 내보내면, harness가 자라나는 prefix를 leniently 파싱하고, incremental하게 validate하며, 확정된 checkpoint를 **lock**한다. 그리고 한 턴이 `length` 한계에 닿으면 그 lock으로부터 새 completion을 이어 붙인다 — 그리하여 하나의 논리적 답변이 단일 completion을 초과할 수 있다. +2. **incremental하게 validate한다.** 업계 표준 `generate-all → validate-once`가 아니라 `generate → parse partial → validate prefix → lock → continue`다. +3. **`for await`로 소비한다.** `conversate()`는 response part의 discriminated union을 담은 async-iterable을 반환하고, 각 part는 그 자체로 더 파고들 수 있다(`text.stream()`, `tool.execute()`). content를 소비하는 데 callback registry가 없다. + +철학은 두 편의 typia harness 아티클(lenient parsing, incremental validation, CoT compliance)에서, 소비 모델은 Claude Code의 async-generator loop에서, connector/orchestration 어휘는 agentica에서, adapter 형태는 Vercel의 `LanguageModelV2`와 BAML의 schema-aligned parsing에서 가져왔다. 출처 분석은 `.wiki/10`–`.wiki/50` 참고. + +## 2. 표준 use-case (사양) + +```ts +const agent = new TypiaAgent({ adapter, controllers }); +const response = await agent.conversate("Yaho~"); +for await (const r of response) { + if (r.type === "tool") { + if ((await r.success()) === false) await r.feedback(); // 교정 + else await r.execute(); // 실행 + } else if (r.type === "text") { + for await (const piece of r.stream()) console.log("piece", piece); + } +} +const { histories, usage, stop } = await response.join(); +``` + +`src/`의 모든 타입은 이 루프를 타입 안전하고, 자동완성되고, 자명하게 만들기 위해 존재하며, 동시에 성장의 여지를 남긴다. + +## 3. 계층 아키텍처 + +``` +┌──────────────────────────────────────────────────────────────────────┐ +│ TypiaAgent (IAgent) facade: conversate(), getHistories, on/off │ +├──────────────────────────────────────────────────────────────────────┤ +│ Orchestration turn loop · selection · describe │ 교체 가능 +├──────────────────────────────────────────────────────────────────────┤ +│ Harness (제품의 본질) stream → lenient parse → incremental │ typia-native, +│ validate → lock → feedback → continue │ 벤더-무관 +├──────────────────────────────────────────────────────────────────────┤ +│ Adapter (IAgentAdapter) 중립 chat-stream primitive │ OpenAI | Vercel | … +└──────────────────────────────────────────────────────────────────────┘ +``` + +harness가 차별화된 핵심이고, adapter는 얇은 provider 시임이며, orchestration은 그 위의 얇은 계층이다. (조사한 모든 프레임워크가 결국 streaming-chat primitive 위의 얇은 loop였다 — 그래서 우리는 primitive에 투자한다.) + +### harness, 단계별 + +1. agent는 모델에게 자신의 결정을 typia 스키마(보통 discriminated union: *speak* vs *call F* — CoT-compliance 아티클이 reasoning을 강제할 때 쓰는 바로 그 패턴)에 맞는 **streamed text**로 내보내게 한다. +2. 토큰이 도착하는 대로 harness는 raw 텍스트를 누적하고, **debounce된 주기**(`IAgentConfig.validateCadence`, 기본 `"boundary"`. 매 토큰은 O(n²)라 금물)에 typia의 **lenient parser**를 돌려 `DeepPartial`를 얻는다. +3. 판별자가 확정되면 harness가 해당 part(`IAgentText` 또는 `IAgentTool`)를 **열고** 이후 delta를 거기로 라우팅한다. +4. typia 생성 validator로 **prefix를 validate**하며, 매 tick을 `IAgentValidation.watch()`(`phase: "streaming" | "validated" | "locked" | "failed"`)로 노출하고, validated checkpoint를 **lock**한다. +5. 구조 중간에 `length` finish가 오면 locked prefix로부터 **이어 붙이고**(`IAgentConfig.continuation`) `usage.continuations`를 올린다. +6. validation 실패 시 `IAgentTool.feedback()`이 에러를 `LlmJson.stringify`로 annotated JSON 렌더하여(아티클의 feedback loop) 재요청하며 `IAgentConfig.retry`로 제한된다. + +결정적으로, harness는 parse/validate/coerce를 **재구현하지 않는다**. `typia.llm.*`의 각 `ILlmFunction` / `ILlmStructuredOutput`이 *같은* 타입에서 생성된 그 클로저를 이미 지닌다. `@typia/agent`는 그것을 sequencing할 뿐이다. 상류 표면은 `.wiki/20` 참고. + +## 4. 타입 카탈로그 (파일 맵) + +타입 하나당 파일 하나. namespace에 변형을 쑤셔넣지 않는다. union 타입은 독립 파일들을 import해 합치는 얇은 파일이고, 변형 interface는 각자의 파일을 가진다. 총 69개 파일. + +### 루트 +- `IAgent.ts` — `IAgent` (계약) +- `TypiaAgent.ts` — `TypiaAgent` (`declare class implements IAgent`) + +### structures/ — 생성 & 배선 +- `IAgentProps.ts`, `IAgentConfig.ts`, `IAgentSystemPrompt.ts`, `IAgentContinuation.ts`, `IAgentCompaction.ts`, `IAgentConversateOptions.ts` + +### structures/ — controllers & operations +- controller union: `IAgentController.ts` → `IAgentClassController.ts` · `IAgentHttpController.ts` · `IAgentMcpController.ts` (+ `IAgentClassExecuteProps.ts` · `IAgentHttpExecuteProps.ts`) +- operation union: `IAgentOperation.ts` → `IAgentClassOperation.ts` · `IAgentHttpOperation.ts` · `IAgentMcpOperation.ts` · `IAgentOutputOperation.ts` (+ 공통 헤드 `IAgentOperationBase.ts`) +- selector: `IAgentSelector.ts` · `IAgentSelectorProps.ts` + +### structures/ — adapter (벤더 중립 시임) +- `IAgentAdapter.ts` · `IAgentAdapterRequest.ts` · `IAgentAdapterCapabilities.ts` +- chunk union: `IAgentChunk.ts` → `IAgentTextDeltaChunk.ts` · `IAgentFinishChunk.ts` · `IAgentErrorChunk.ts` · `IAgentRawChunk.ts` (+ `IAgentFinishReason.ts`) + +### structures/ — messages & usage +- `IAgentMessage.ts`; content union `IAgentMessageContent.ts` → `IAgentTextContent.ts` · `IAgentImageContent.ts` · `IAgentFileContent.ts` · `IAgentAudioContent.ts` +- `IAgentTokenUsage.ts` (+ `IAgentTokenUsageInput.ts` · `IAgentTokenUsageOutput.ts`) + +### responses/ — streamed 턴 +- `IAgentExecution.ts` — `AsyncIterable` + `join()`/`abort()` +- response union: `IAgentResponse.ts` → `IAgentText.ts` · `IAgentTool.ts` (+ `IAgentFeedbackProps.ts`) +- incremental validation: `IAgentValidation.ts` (+ `IAgentValidationState.ts`) +- `IAgentExecute.ts` · `IAgentTurn.ts` + +### histories/ — 메모리 +- live union: `IAgentHistory.ts` → `IAgentTextHistory.ts` · `IAgentToolHistory.ts` · `IAgentSystemHistory.ts` +- JSON union: `IAgentHistoryJson.ts` → `IAgentTextHistoryJson.ts` · `IAgentToolHistoryJson.ts` · `IAgentSystemHistoryJson.ts` (+ 공통 헤드 `IAgentHistoryBase.ts`) + +### events/ — telemetry (부차) +- union: `IAgentEvent.ts` → `IAgentRequestEvent.ts` · `IAgentResponseEvent.ts` · `IAgentValidateEvent.ts` · `IAgentParseEvent.ts` · `IAgentContinuationEvent.ts` · `IAgentUsageEvent.ts` (+ `IAgentEventBase.ts` · `IAgentEventMapper.ts` · `IAgentEventSource.ts`) + +### 하중을 견디는 세 타입 + +- **`IAgentExecution`** — 그 자체가 async-iterable이다. iterator는 `IAgentResponse`를 **yield**하고 `IAgentTurn`을 **return**한다(Claude Code의 `AsyncGenerator`). `for await`가 part를 소비하고, `join()`이 결과를 회수한다. lazy/pull 주도라, `tool` part가 소비자의 결정을 위해 턴 재개 전에 멈출 수 있다. +- **`IAgentTool`** — streamed payload 위의 소비자 주도 state machine이며 `IAgentValidation`을 상속한다. `success()`/`validate()`로 점검, `feedback()`은 제한된 correction loop를 돌려 *교정된* tool을 resolve(chainable), `execute()`는 바인딩된 함수를 실행(소비자 게이팅 — permission / human-in-the-loop의 자연스러운 자리). +- **`IAgentValidation`** — incremental validation 표면: `snapshot()`(최신 `DeepPartial`), `locked()`(truncation을 견디는 checkpoint), `watch()`(BAML식 `phase`를 가진 typed *이며 validated*된 partial), `validate()`/`success()`. + +## 5. 벤더 중립 adapter + +`IAgentAdapter`는 어떤 backend든 하나의 메서드로 환원한다. + +```ts +stream(input: IAgentAdapterRequest): AsyncIterable; +// IAgentChunk = text-delta | finish | error | raw +``` + +Vercel의 `LanguageModelV2.doStream`을 본떴으되 **더 작다** — harness가 *텍스트*를 streaming하므로 chunk union은 `text-delta` + `finish`가 지배하고, provider 고유 이벤트(citations, logprobs 등)는 `raw` escape hatch로 흘려 최저공통분모 함정을 피한다. native `tools`는 광고된 선택 capability(`IAgentAdapterCapabilities`)이지 핵심이 아니다. + +모든 provider 특이성은 adapter **내부**에서 정규화된다(OpenAI의 "첫 tool-call delta에만 id/name", `stream_options.include_usage`, Vercel의 major 버전 간 식별자 변동). typia의 `ILlmSchema`가 vendor 통합형(`typia@13`에서 모델별 schema 변형 없음)이라, adapter가 provider 지식이 사는 *유일한* 장소다. OpenAI ⇄ Vercel 교체는 `IAgentProps.adapter` 한 줄이고 agent 코드는 그대로다. + +**해결된 질문:** Vercel AI SDK는 저수준 *stateless* primitive다(내장 메모리 없음). 그래서 어떤 adapter를 쓰든 `@typia/agent`가 history/메모리를 직접 소유한다(`IAgentHistory`). adapter는 request→stream만 한다. `.wiki/50` 참고. + +## 6. 메모리 + +상태는 `IAgentHistory` 배열이며 매 턴 (`IAgentMessage[]`로 재구성되어) 재생된다 — agentica 모델. live 형태는 런타임 객체를 참조할 수 있고, 직렬화 mirror `IAgentHistoryJson`이 영속화·재개(`IAgentProps.histories`) 대상이다. v1은 전체 history를 재생한다. `IAgentConfig.compaction`은 긴 호흡에서 summarize-and-truncate를 위한 예약 hook이다(Claude Code가 보이듯 규모가 커지면 중요해진다 — `.wiki/40`). + +## 7. 함수가 많을 때의 확장 + +기본 agent는 **micro**다. selector가 없고 매 턴 모든 함수를 나열한다 — 수십 개까지 이상적. `IAgentConfig.capacity`를 넘으면 `IAgentSelector`가 개입하며, 세 가지 조합 가능한 전략을 둔다(`.wiki/60` D9). + +1. **Semantic pre-filter**(`IAgentSelector.prefilter`) — 함수 description을 embedding해 사용자 메시지와 유사한 것만, LLM 호출 이전에 남긴다. +2. **Capacity divide-and-conquer** — 생존자를 그룹으로 쪼개 그룹별 병렬 선택, elitism 재선택은 선택(agentica 방식). +3. **Incremental-validation selection** — 선택 자체를 enum/`MinItems` 스키마에 대해 incremental하게 validate되는 streamed structured output으로 돌린다(dog-fooding). + +HTTP controller(`IAgentHttpController`, `HttpLlm` 경유)는 REST 백엔드 전체를 함수로 바꾼다 — "함수가 아주 많은" 흔한 출처다. + +## 8. 선행 기술과의 차이 + +| 관심사 | agentica | Vercel AI SDK | `@typia/agent` | +| --- | --- | --- | --- | +| 턴 반환 | `Promise` + `on/off` 이벤트 | `streamText` view | **`AsyncIterable`** + `join()` | +| function calling | native `tool_calls` | native `tool_calls` | **streamed text + lenient parse** | +| output-token 천장 | — | — | **checkpoint-lock + continue** | +| partial validation | post-hoc | typed but **미검증** 스냅샷 | **incremental, validated** partial | +| 벤더 결합 | 하드 OpenAI | 중립 `LanguageModelV2` | **중립 `IAgentAdapter`** | +| 메모리 | full-replay history | 없음(stateless) | full-replay history (+ compaction hook) | +| 많은 함수 | `capacity` divide-and-conquer | — | `IAgentSelector` (prefilter + D&C + streamed) | + +## 9. 검증 상태 + +- `tsgo --noEmit -p packages/agent/tsconfig.json` → **클린**(전체 타입 표면이 내부적으로 정합하며 `@typia/interface`에 대해 조합된다). +- `prettier --write` 적용(import 정렬 + repo의 `prettier-plugin-jsdoc`). markdown은 prettier 대상이 아니라 줄바꿈 없이 유지된다. +- 런타임/테스트는 아직 없다 — 이번 작업은 타입 + 문서 전용(mandate대로). + +## 10. 미해결 질문 + +`.wiki/70-open-questions.md`에서 추적한다: 정확한 continuation-seeding 프로토콜(prefill vs remainder, provider별), iterator 포기 시 teardown(`onUnhandledTool`), parallel tool 실행, history compaction 소유 주체, 능동적 토큰 추정, 멀티모달 *출력*, adapter capability 협상, 에러 분류 체계, 그리고 structured-output이 별도 response 멤버를 가질지(현재는 `IAgentOutputOperation`을 통해 `IAgentTool`에 접힘). + +## 11. 다음 단계 (구현 단계) + +1. `IAgentAdapter`에 대해 `OpenAiAdapter`와 `VercelAdapter` 구현. +2. harness 구현: streaming 버퍼 + debounce된 lenient-parse + incremental validate + checkpoint lock + continuation, `ILlmFunction` 클로저 sequencing. +3. `IAgentExecution`을 생산하는 lazy orchestration loop 구현. +4. parallel tool을 위해 Claude Code의 `Stream` callback↔async-iterable bridge와 bounded concurrent-generator merge 이식(`.wiki/40` L5). +5. suite 컨벤션을 미러링한 `tests/test-agent/` 하위 function-per-file 테스트(`.codex/skills/development`). diff --git a/packages/agent/INSTRUCTION.md b/packages/agent/INSTRUCTION.md new file mode 100644 index 00000000000..a73f7674b5e --- /dev/null +++ b/packages/agent/INSTRUCTION.md @@ -0,0 +1,36 @@ +나는 `@typia/agent` 라이브러리를 만들고자 하노라. + +다음 두 가지 아티클을 핵심 철학으로 삼을 것이며, 이 중 특히 Lenient JSDN parsing + function calling 내지 structured output의 streaming을 위해 달리는 것이니라. + +- https://typia.io/blog/function-calling-harness-qwen-meetup-korea/ +- https://typia.io/blog/function-calling-harness-2-cot-compliance/ + +그리고 `D:/github/wrtnlabs/agentica`를 보면, 이벤트에 기반하여 functino calling 프레임워크를 만들었음을 알 수 있다. `@typia/agent`도 이 중 `MicroAgentica`와 같은 기능을 만들 것이되, agentica처럼 tools 기능을 쓰는게 아니라, text streaming response를 통해 출력 가능 최대 토큰량의 제한을 뛰어넘을 것이며, function callling harness에 나오는 incremental validation을 이룩할 것이다. + +더하여 대략 아래와 같은 유즈케이스를 이룩하고자 하노라. `for await` statement와 discriminated union 타입을 아주 적극적으로 활용하고자 함이니. + +```typescript +const agent = new TypiaAgent({ ... }); +const response = await agent.conversate("Yaho~"); +for await (const r of response) { + if (r.type === "tool") { + if (await r.success() === false) { + await r.feedback(); + } else { + await r.execute(); + } + } else if (r.type === "text") { + for await (const piece of r.stream()) { + consoole.log("piece", piece); + } + } +} +``` + +agentica처럼 OpenAI SDK를 사용하는 형태도 좋고, vercel AI SDK를 사용하는 형태도 좋고, 둘 다 사용 가능한 어댑터여도 좋다. 가장 중요한것은 이러한 인터페이스 설계를 얼마나 합리적으로 하냐겠지? 간결명료하여 누구나 쉽게 사용할 수 있으면서도 확장성 있는 인터페이스, 그것이 너가 가장 처음에 할 일이다. + +따라서 `".wiki"` 폴더를 만들고, 거기에 다양한 래퍼런스 자료를 조사하고 사고 실험해가며, 지식대백과를 누적하라. 그리고 최고의 인터페이스를 만들어, 그야말로 모든게 다 가능해지게 하여라. Agentica 외 모던 AI 프레임워크들도 조사해보고, 필요하면 git clone 하여 소스코드도 까 보고 (`".repositories/"` 폴더에 클론하면 gitignore 되어있어서 괜찮음), 때때로 Claude Code의 소스코드도 분석하려무나(`D:/github/contributions/claude-code`). + +이래저래 OpenAI SDK를 쓰더라도, Vercel SDK는 좋은 참고자료가 될 것이다. 이게 메모리까지 제공하는지 아니면 단순 저수준 API만 제공하는지 잘 모르겠다만, 모든 가능성을 열어두고 조사하라. 물론, `@typia/agent`는 function calling harness가 메인이니, 이 부분이 가장 잘 다루어져야 할 것이다. 번외로 함수의 수가 엄청 많을 때, 이를 어찌 최적화할지도 고민해보거라. + +그리하여 최고 품질의 `@typia/agent` 아키텍처이자 인터페이스를 도출해보니라. 그리고 그것을 packages/agent에 그야말로 인터페이스 타입으로써만 정의해보거라. 각각 타입마다 독립 파일을 가지면 되노니, 최선을 다하여보거라. 또한 `packages/agent/ARCHITECTURE.md`에, 이 모든걸 총정리한 보고서를 쓰거라. diff --git a/packages/agent/package.json b/packages/agent/package.json new file mode 100644 index 00000000000..b8f6c4cce4f --- /dev/null +++ b/packages/agent/package.json @@ -0,0 +1,77 @@ +{ + "name": "@typia/agent", + "version": "13.0.0-dev.20260605.4", + "description": "AI agent integration for typia", + "main": "src/index.ts", + "exports": { + ".": "./src/index.ts", + "./package.json": "./package.json" + }, + "scripts": { + "build": "rimraf lib && ttsc && rollup -c", + "dev": "ttsc --watch", + "prepack": "pnpm run build" + }, + "repository": { + "type": "git", + "url": "https://github.com/samchon/typia" + }, + "author": "Jeongho Nam", + "license": "MIT", + "bugs": { + "url": "https://github.com/samchon/typia/issues" + }, + "homepage": "https://typia.io", + "dependencies": { + "@typia/interface": "workspace:^", + "@typia/utils": "workspace:^", + "typia": "workspace:^" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "catalog:rollup", + "@rollup/plugin-node-resolve": "catalog:rollup", + "@typescript/native-preview": "catalog:typescript", + "rimraf": "catalog:utils", + "rollup": "catalog:rollup", + "rollup-plugin-auto-external": "catalog:rollup", + "rollup-plugin-node-externals": "catalog:rollup", + "tinyglobby": "^0.2.12", + "ttsc": "catalog:typescript" + }, + "sideEffects": false, + "files": [ + "README.md", + "INSTRUCTION.md", + "package.json", + "lib", + "src" + ], + "keywords": [ + "agent", + "typia", + "llm", + "llm-function-calling", + "ai", + "claude", + "openai", + "chatgpt", + "gemini", + "validation", + "json-schema" + ], + "packageManager": "pnpm@10.6.4", + "publishConfig": { + "access": "public", + "main": "lib/index.js", + "module": "lib/index.mjs", + "types": "lib/index.d.ts", + "exports": { + ".": { + "types": "./lib/index.d.ts", + "import": "./lib/index.mjs", + "default": "./lib/index.js" + }, + "./package.json": "./package.json" + } + } +} diff --git a/packages/agent/rollup.config.mjs b/packages/agent/rollup.config.mjs new file mode 100644 index 00000000000..077e7f4a1e1 --- /dev/null +++ b/packages/agent/rollup.config.mjs @@ -0,0 +1 @@ +export { default } from "../../config/rollup.config.mjs"; diff --git a/packages/agent/src/IAgent.ts b/packages/agent/src/IAgent.ts new file mode 100644 index 00000000000..88e96abc503 --- /dev/null +++ b/packages/agent/src/IAgent.ts @@ -0,0 +1,71 @@ +import { IAgentEventMapper } from "./events/IAgentEventMapper"; +import { IAgentHistory } from "./histories/IAgentHistory"; +import { IAgentExecution } from "./responses/IAgentExecution"; +import { IAgentAdapter } from "./structures/IAgentAdapter"; +import { IAgentConfig } from "./structures/IAgentConfig"; +import { IAgentController } from "./structures/IAgentController"; +import { IAgentConversateOptions } from "./structures/IAgentConversateOptions"; +import { IAgentMessageContent } from "./structures/IAgentMessageContent"; +import { IAgentTokenUsage } from "./structures/IAgentTokenUsage"; + +/** + * Agent의 계약(contract) — {@link TypiaAgent}가 구현하는 형태. + * + * 중요한 메서드는 하나, {@link conversate}다. 나머지는 읽기 측 accessor(history, usage, + * controllers, config, adapter), 부차 이벤트 채널({@link on}/{@link off}), 그리고 + * {@link clone}이다. 계약을 인터페이스로 두면 selector 없는 "micro" agent와 selector 구동 "full" + * agent가 둘 다 이를 만족할 수 있고, 테스트/모킹이 구체 클래스를 대신할 수 있다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgent { + /** + * 사용자 메시지를 보내고 agent의 응답을 `for await`용으로 streaming한다. + * + * {@link IAgentExecution}을 반환한다 — 그 자체가 {@link IAgentResponse} part의 + * async-iterable이다. 외부 `Promise`는 턴이 _시작_되면(첫 요청 발신) resolve되므로 곧바로 순회할 수 + * 있다: + * + * ```ts + * const execution = await agent.conversate("Yaho~"); + * for await (const r of execution) { ... } + * ``` + * + * @param content 일반 텍스트, 또는 멀티모달 content(text/image/file/audio). + * @param options 턴 단위 옵션(abort signal). + */ + conversate( + content: string | IAgentMessageContent | IAgentMessageContent[], + options?: IAgentConversateOptions, + ): Promise; + + /** 지금까지의 대화(live history). */ + getHistories(): IAgentHistory[]; + + /** 대화 전체의 누적 토큰 usage. */ + getTokenUsage(): IAgentTokenUsage; + + /** 등록된 함수 controller들. */ + getControllers(): IAgentController[]; + + /** 적용된 설정(기본값 병합). */ + getConfig(): IAgentConfig | undefined; + + /** 사용 중인 LLM adapter. */ + getAdapter(): IAgentAdapter; + + /** Telemetry {@link IAgentEvent}(부차 채널)를 구독한다. 체이닝을 위해 `this` 반환. */ + on( + type: Type, + listener: (event: IAgentEventMapper[Type]) => void | Promise, + ): this; + + /** 이전에 등록한 listener를 해제한다. */ + off( + type: Type, + listener: (event: IAgentEventMapper[Type]) => void | Promise, + ): this; + + /** 같은 config/controllers와 history 스냅샷을 가진 깊은 복제본. */ + clone(): IAgent; +} diff --git a/packages/agent/src/TypiaAgent.ts b/packages/agent/src/TypiaAgent.ts new file mode 100644 index 00000000000..a06333e801a --- /dev/null +++ b/packages/agent/src/TypiaAgent.ts @@ -0,0 +1,80 @@ +import { IAgent } from "./IAgent"; +import { IAgentEventMapper } from "./events/IAgentEventMapper"; +import { IAgentHistory } from "./histories/IAgentHistory"; +import { IAgentExecution } from "./responses/IAgentExecution"; +import { IAgentAdapter } from "./structures/IAgentAdapter"; +import { IAgentConfig } from "./structures/IAgentConfig"; +import { IAgentController } from "./structures/IAgentController"; +import { IAgentConversateOptions } from "./structures/IAgentConversateOptions"; +import { IAgentMessageContent } from "./structures/IAgentMessageContent"; +import { IAgentProps } from "./structures/IAgentProps"; +import { IAgentTokenUsage } from "./structures/IAgentTokenUsage"; + +/** + * Typia harness 위에 구축된, streaming function-calling agent — `@typia/agent`의 + * `MicroAgentica` 등가물이자 mandate의 use-case가 인스턴스화하는 클래스: + * + * ```ts + * const agent = new TypiaAgent({ adapter, controllers }); + * const response = await agent.conversate("Yaho~"); + * for await (const r of response) { + * if (r.type === "tool") { + * if ((await r.success()) === false) await r.feedback(); + * else await r.execute(); + * } else if (r.type === "text") { + * for await (const piece of r.stream()) console.log("piece", piece); + * } + * } + * ``` + * + * 기본적으로 selector가 **없다**(매 턴 모든 함수 나열, `MicroAgentica`처럼). + * {@link IAgentConfig.capacity} + selector를 두면 함수가 많아도 확장된다. provider의 native + * `tools` feature를 쓰지 **않는다**. structured _텍스트_를 streaming하고, lenient parsing으로 + * 복구하며, incremental하게 validate하고, locked checkpoint로부터 output-token 천장을 넘어 이어 + * 붙인다. + * + * > 여기서는 인터페이스 전용 `declare class`(런타임 본문 없음)로 선언된다 — 이번 작업은 타입 표면을 출하하고, 구현은 별도로 + * > 따라온다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export declare class TypiaAgent implements IAgent { + /** @param props 생성 속성 — {@link IAgentProps.adapter}만 필수. */ + public constructor(props: IAgentProps); + + /** @inheritdoc */ + public conversate( + content: string | IAgentMessageContent | IAgentMessageContent[], + options?: IAgentConversateOptions, + ): Promise; + + /** @inheritdoc */ + public getHistories(): IAgentHistory[]; + + /** @inheritdoc */ + public getTokenUsage(): IAgentTokenUsage; + + /** @inheritdoc */ + public getControllers(): IAgentController[]; + + /** @inheritdoc */ + public getConfig(): IAgentConfig | undefined; + + /** @inheritdoc */ + public getAdapter(): IAgentAdapter; + + /** @inheritdoc */ + public on( + type: Type, + listener: (event: IAgentEventMapper[Type]) => void | Promise, + ): this; + + /** @inheritdoc */ + public off( + type: Type, + listener: (event: IAgentEventMapper[Type]) => void | Promise, + ): this; + + /** @inheritdoc */ + public clone(): TypiaAgent; +} diff --git a/packages/agent/src/events/IAgentContinuationEvent.ts b/packages/agent/src/events/IAgentContinuationEvent.ts new file mode 100644 index 00000000000..a44c9b2ad1e --- /dev/null +++ b/packages/agent/src/events/IAgentContinuationEvent.ts @@ -0,0 +1,14 @@ +import { IAgentEventBase } from "./IAgentEventBase"; + +/** + * Ceiling-continuation이 수행됨(출력이 단일 completion을 초과). + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentContinuationEvent extends IAgentEventBase<"continuation"> { + /** 현재 답변 내 이 continuation의 1-based 인덱스. */ + index: number; + + /** 이를 trigger한 finish 사유(대개 `"length"`). */ + trigger: string; +} diff --git a/packages/agent/src/events/IAgentEvent.ts b/packages/agent/src/events/IAgentEvent.ts new file mode 100644 index 00000000000..593a4b4b661 --- /dev/null +++ b/packages/agent/src/events/IAgentEvent.ts @@ -0,0 +1,27 @@ +import { IAgentContinuationEvent } from "./IAgentContinuationEvent"; +import { IAgentParseEvent } from "./IAgentParseEvent"; +import { IAgentRequestEvent } from "./IAgentRequestEvent"; +import { IAgentResponseEvent } from "./IAgentResponseEvent"; +import { IAgentUsageEvent } from "./IAgentUsageEvent"; +import { IAgentValidateEvent } from "./IAgentValidateEvent"; + +/** + * **부차적인**, 횡단 telemetry 채널 — 주 content 스트림({@link IAgentExecution})과 구분된다. + * + * Async-iterable은 _대화 content_(text, tool 호출)를 나른다. 이벤트는 직접 계측해야 했을 + * *배관(plumbing)*을 나른다. 즉 raw adapter request/response, 매 validation 시도, 매 + * lenient-parse 복구, usage 갱신, ceiling-continuation. content는 의도적으로 여기에 **중복되지 + * 않는다** — 같은 메시지가 두 채널로 오는 "event soup"를 피한다. + * {@link IAgent.on}/{@link IAgent.off}로 구독한다. + * + * `type`으로 판별된다. {@link IAgentEventMapper}가 `on`/`off`의 타입별 listener 타이핑을 준다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export type IAgentEvent = + | IAgentRequestEvent + | IAgentResponseEvent + | IAgentValidateEvent + | IAgentParseEvent + | IAgentContinuationEvent + | IAgentUsageEvent; diff --git a/packages/agent/src/events/IAgentEventBase.ts b/packages/agent/src/events/IAgentEventBase.ts new file mode 100644 index 00000000000..7430852a786 --- /dev/null +++ b/packages/agent/src/events/IAgentEventBase.ts @@ -0,0 +1,16 @@ +/** + * 모든 {@link IAgentEvent} 변형이 공유하는 공통 헤드. + * + * @author Jeongho Nam - https://github.com/samchon + * @template Type 판별자 리터럴. + */ +export interface IAgentEventBase { + /** 판별자(discriminator). */ + type: Type; + + /** 고유 id. */ + id: string; + + /** ISO-8601 생성 시각. */ + created_at: string; +} diff --git a/packages/agent/src/events/IAgentEventMapper.ts b/packages/agent/src/events/IAgentEventMapper.ts new file mode 100644 index 00000000000..acbd5676b1f --- /dev/null +++ b/packages/agent/src/events/IAgentEventMapper.ts @@ -0,0 +1,20 @@ +import { IAgentContinuationEvent } from "./IAgentContinuationEvent"; +import { IAgentParseEvent } from "./IAgentParseEvent"; +import { IAgentRequestEvent } from "./IAgentRequestEvent"; +import { IAgentResponseEvent } from "./IAgentResponseEvent"; +import { IAgentUsageEvent } from "./IAgentUsageEvent"; +import { IAgentValidateEvent } from "./IAgentValidateEvent"; + +/** + * 판별자 → 이벤트 매핑. {@link IAgent.on}/{@link IAgent.off}의 타입 안전 listener 디스패치에 쓰인다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentEventMapper { + request: IAgentRequestEvent; + response: IAgentResponseEvent; + validate: IAgentValidateEvent; + parse: IAgentParseEvent; + continuation: IAgentContinuationEvent; + usage: IAgentUsageEvent; +} diff --git a/packages/agent/src/events/IAgentEventSource.ts b/packages/agent/src/events/IAgentEventSource.ts new file mode 100644 index 00000000000..268fefd8a1f --- /dev/null +++ b/packages/agent/src/events/IAgentEventSource.ts @@ -0,0 +1,6 @@ +/** + * 이벤트를 내보낸 orchestration 단계. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export type IAgentEventSource = "select" | "call" | "describe" | "output"; diff --git a/packages/agent/src/events/IAgentParseEvent.ts b/packages/agent/src/events/IAgentParseEvent.ts new file mode 100644 index 00000000000..ff33ee27578 --- /dev/null +++ b/packages/agent/src/events/IAgentParseEvent.ts @@ -0,0 +1,14 @@ +import { IJsonParseResult } from "@typia/interface"; + +import { IAgentOperation } from "../structures/IAgentOperation"; +import { IAgentEventBase } from "./IAgentEventBase"; + +/** + * Lenient-parse 복구(Tier-1 seam) — 성공 또는 partial. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentParseEvent extends IAgentEventBase<"parse"> { + operation: IAgentOperation | null; + result: IJsonParseResult; +} diff --git a/packages/agent/src/events/IAgentRequestEvent.ts b/packages/agent/src/events/IAgentRequestEvent.ts new file mode 100644 index 00000000000..6c47bdb6722 --- /dev/null +++ b/packages/agent/src/events/IAgentRequestEvent.ts @@ -0,0 +1,13 @@ +import { IAgentMessage } from "../structures/IAgentMessage"; +import { IAgentEventBase } from "./IAgentEventBase"; +import { IAgentEventSource } from "./IAgentEventSource"; + +/** + * Adapter로 나간 요청(우리가 보낸 중립 메시지). + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentRequestEvent extends IAgentEventBase<"request"> { + source: IAgentEventSource; + messages: IAgentMessage[]; +} diff --git a/packages/agent/src/events/IAgentResponseEvent.ts b/packages/agent/src/events/IAgentResponseEvent.ts new file mode 100644 index 00000000000..5d034edccdd --- /dev/null +++ b/packages/agent/src/events/IAgentResponseEvent.ts @@ -0,0 +1,14 @@ +import { IAgentTokenUsage } from "../structures/IAgentTokenUsage"; +import { IAgentEventBase } from "./IAgentEventBase"; +import { IAgentEventSource } from "./IAgentEventSource"; + +/** + * 보고된 usage를 담은, 완료된 adapter 응답. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentResponseEvent extends IAgentEventBase<"response"> { + source: IAgentEventSource; + usage: IAgentTokenUsage; + finishReason: string; +} diff --git a/packages/agent/src/events/IAgentUsageEvent.ts b/packages/agent/src/events/IAgentUsageEvent.ts new file mode 100644 index 00000000000..64e6e429854 --- /dev/null +++ b/packages/agent/src/events/IAgentUsageEvent.ts @@ -0,0 +1,11 @@ +import { IAgentTokenUsage } from "../structures/IAgentTokenUsage"; +import { IAgentEventBase } from "./IAgentEventBase"; + +/** + * 누적 usage 갱신. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentUsageEvent extends IAgentEventBase<"usage"> { + usage: IAgentTokenUsage; +} diff --git a/packages/agent/src/events/IAgentValidateEvent.ts b/packages/agent/src/events/IAgentValidateEvent.ts new file mode 100644 index 00000000000..60bb6b45dff --- /dev/null +++ b/packages/agent/src/events/IAgentValidateEvent.ts @@ -0,0 +1,16 @@ +import { IValidation } from "@typia/interface"; + +import { IAgentOperation } from "../structures/IAgentOperation"; +import { IAgentEventBase } from "./IAgentEventBase"; + +/** + * Streamed prefix 또는 최종 값에 대한 validation 시도. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentValidateEvent extends IAgentEventBase<"validate"> { + operation: IAgentOperation | null; + result: IValidation; + /** Incremental(prefix) validation인지 final validation인지. */ + incremental: boolean; +} diff --git a/packages/agent/src/histories/IAgentHistory.ts b/packages/agent/src/histories/IAgentHistory.ts new file mode 100644 index 00000000000..b234723bb38 --- /dev/null +++ b/packages/agent/src/histories/IAgentHistory.ts @@ -0,0 +1,19 @@ +import { IAgentSystemHistory } from "./IAgentSystemHistory"; +import { IAgentTextHistory } from "./IAgentTextHistory"; +import { IAgentToolHistory } from "./IAgentToolHistory"; + +/** + * 하나의 대화 사건의 live 기록 — agent 메모리의 단위. + * + * `@typia/agent`의 상태는 history 배열이며, 매 턴 (중립 메시지로 재구성되어) 재생된다 (agentica 모델). + * live 형태는 런타임 객체를 참조할 수 있고, 직렬화 mirror는 {@link IAgentHistoryJson}이며 이것이 + * 영속화·재개({@link IAgentProps.histories}) 대상이다. + * + * `type`으로 판별된다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export type IAgentHistory = + | IAgentTextHistory + | IAgentToolHistory + | IAgentSystemHistory; diff --git a/packages/agent/src/histories/IAgentHistoryBase.ts b/packages/agent/src/histories/IAgentHistoryBase.ts new file mode 100644 index 00000000000..5a8d870931a --- /dev/null +++ b/packages/agent/src/histories/IAgentHistoryBase.ts @@ -0,0 +1,16 @@ +/** + * 모든 {@link IAgentHistory} 및 {@link IAgentHistoryJson} 변형이 공유하는 공통 헤드. + * + * @author Jeongho Nam - https://github.com/samchon + * @template Type 판별자 리터럴. + */ +export interface IAgentHistoryBase { + /** 판별자(discriminator). */ + type: Type; + + /** 고유 id. */ + id: string; + + /** ISO-8601 생성 시각. */ + created_at: string; +} diff --git a/packages/agent/src/histories/IAgentHistoryJson.ts b/packages/agent/src/histories/IAgentHistoryJson.ts new file mode 100644 index 00000000000..43c4b0fdf95 --- /dev/null +++ b/packages/agent/src/histories/IAgentHistoryJson.ts @@ -0,0 +1,14 @@ +import { IAgentSystemHistoryJson } from "./IAgentSystemHistoryJson"; +import { IAgentTextHistoryJson } from "./IAgentTextHistoryJson"; +import { IAgentToolHistoryJson } from "./IAgentToolHistoryJson"; + +/** + * {@link IAgentHistory}의 직렬화 mirror — live 객체 없이 `JSON.stringify` 가능하며 + * {@link IAgentProps.histories}로 round-trip한다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export type IAgentHistoryJson = + | IAgentTextHistoryJson + | IAgentToolHistoryJson + | IAgentSystemHistoryJson; diff --git a/packages/agent/src/histories/IAgentSystemHistory.ts b/packages/agent/src/histories/IAgentSystemHistory.ts new file mode 100644 index 00000000000..f3fd18f1c43 --- /dev/null +++ b/packages/agent/src/histories/IAgentSystemHistory.ts @@ -0,0 +1,11 @@ +import { IAgentHistoryBase } from "./IAgentHistoryBase"; + +/** + * 대화에 주입된 system/developer 지시의 history 기록. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentSystemHistory extends IAgentHistoryBase<"system"> { + /** 지시 본문. */ + content: string; +} diff --git a/packages/agent/src/histories/IAgentSystemHistoryJson.ts b/packages/agent/src/histories/IAgentSystemHistoryJson.ts new file mode 100644 index 00000000000..ef91fb467c7 --- /dev/null +++ b/packages/agent/src/histories/IAgentSystemHistoryJson.ts @@ -0,0 +1,10 @@ +import { IAgentHistoryBase } from "./IAgentHistoryBase"; + +/** + * {@link IAgentSystemHistory}의 직렬화 mirror. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentSystemHistoryJson extends IAgentHistoryBase<"system"> { + content: string; +} diff --git a/packages/agent/src/histories/IAgentTextHistory.ts b/packages/agent/src/histories/IAgentTextHistory.ts new file mode 100644 index 00000000000..f0ff3109477 --- /dev/null +++ b/packages/agent/src/histories/IAgentTextHistory.ts @@ -0,0 +1,18 @@ +import { IAgentMessageContent } from "../structures/IAgentMessageContent"; +import { IAgentHistoryBase } from "./IAgentHistoryBase"; + +/** + * 사용자/assistant/describe 텍스트 메시지의 history 기록. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentTextHistory extends IAgentHistoryBase<"text"> { + /** 화자. */ + role: "user" | "assistant"; + + /** Assistant는 `"answer"`/`"describe"`, 사용자는 `"request"`. */ + purpose: "request" | "answer" | "describe"; + + /** 일반 텍스트, 또는 사용자 입력의 멀티모달 content. */ + content: string | IAgentMessageContent[]; +} diff --git a/packages/agent/src/histories/IAgentTextHistoryJson.ts b/packages/agent/src/histories/IAgentTextHistoryJson.ts new file mode 100644 index 00000000000..2c75f26cb91 --- /dev/null +++ b/packages/agent/src/histories/IAgentTextHistoryJson.ts @@ -0,0 +1,13 @@ +import { IAgentMessageContent } from "../structures/IAgentMessageContent"; +import { IAgentHistoryBase } from "./IAgentHistoryBase"; + +/** + * {@link IAgentTextHistory}의 직렬화 mirror. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentTextHistoryJson extends IAgentHistoryBase<"text"> { + role: "user" | "assistant"; + purpose: "request" | "answer" | "describe"; + content: string | IAgentMessageContent[]; +} diff --git a/packages/agent/src/histories/IAgentToolHistory.ts b/packages/agent/src/histories/IAgentToolHistory.ts new file mode 100644 index 00000000000..8ed68cfe2bc --- /dev/null +++ b/packages/agent/src/histories/IAgentToolHistory.ts @@ -0,0 +1,21 @@ +import { IAgentOperation } from "../structures/IAgentOperation"; +import { IAgentHistoryBase } from "./IAgentHistoryBase"; + +/** + * 완료된 함수 호출의 history 기록: 인자 + 실행 결과. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentToolHistory extends IAgentHistoryBase<"tool"> { + /** 호출된 operation(live 참조). */ + operation: IAgentOperation; + + /** Validated 인자. */ + arguments: Record; + + /** 반환값(또는 `"output"` operation의 structured payload). */ + value: unknown; + + /** 실행 성공 여부. */ + success: boolean; +} diff --git a/packages/agent/src/histories/IAgentToolHistoryJson.ts b/packages/agent/src/histories/IAgentToolHistoryJson.ts new file mode 100644 index 00000000000..2f87ccd5c1d --- /dev/null +++ b/packages/agent/src/histories/IAgentToolHistoryJson.ts @@ -0,0 +1,28 @@ +import { IAgentOperation } from "../structures/IAgentOperation"; +import { IAgentHistoryBase } from "./IAgentHistoryBase"; + +/** + * {@link IAgentToolHistory}의 직렬화 mirror — live operation 객체 대신 protocol과 이름만 + * 담는다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentToolHistoryJson extends IAgentHistoryBase<"tool"> { + /** 출처 operation의 protocol. */ + protocol: IAgentOperation["protocol"]; + + /** 출처 controller 이름. */ + controller: string; + + /** 함수 이름. */ + function: string; + + /** Validated 인자. */ + arguments: Record; + + /** 반환값. */ + value: unknown; + + /** 실행 성공 여부. */ + success: boolean; +} diff --git a/packages/agent/src/index.ts b/packages/agent/src/index.ts new file mode 100644 index 00000000000..bafd06fa4f8 --- /dev/null +++ b/packages/agent/src/index.ts @@ -0,0 +1,83 @@ +// the agent +export * from "./IAgent"; +export * from "./TypiaAgent"; + +// structures — construction & wiring +export * from "./structures/IAgentProps"; +export * from "./structures/IAgentConfig"; +export * from "./structures/IAgentSystemPrompt"; +export * from "./structures/IAgentContinuation"; +export * from "./structures/IAgentCompaction"; +export * from "./structures/IAgentConversateOptions"; + +// structures — controllers & operations +export * from "./structures/IAgentController"; +export * from "./structures/IAgentClassController"; +export * from "./structures/IAgentHttpController"; +export * from "./structures/IAgentMcpController"; +export * from "./structures/IAgentClassExecuteProps"; +export * from "./structures/IAgentHttpExecuteProps"; +export * from "./structures/IAgentOperation"; +export * from "./structures/IAgentOperationBase"; +export * from "./structures/IAgentClassOperation"; +export * from "./structures/IAgentHttpOperation"; +export * from "./structures/IAgentMcpOperation"; +export * from "./structures/IAgentOutputOperation"; +export * from "./structures/IAgentSelector"; +export * from "./structures/IAgentSelectorProps"; + +// structures — adapter (vendor-neutral seam) +export * from "./structures/IAgentAdapter"; +export * from "./structures/IAgentAdapterRequest"; +export * from "./structures/IAgentAdapterCapabilities"; +export * from "./structures/IAgentChunk"; +export * from "./structures/IAgentTextDeltaChunk"; +export * from "./structures/IAgentFinishChunk"; +export * from "./structures/IAgentErrorChunk"; +export * from "./structures/IAgentRawChunk"; +export * from "./structures/IAgentFinishReason"; + +// structures — messages & usage +export * from "./structures/IAgentMessage"; +export * from "./structures/IAgentMessageContent"; +export * from "./structures/IAgentTextContent"; +export * from "./structures/IAgentImageContent"; +export * from "./structures/IAgentFileContent"; +export * from "./structures/IAgentAudioContent"; +export * from "./structures/IAgentTokenUsage"; +export * from "./structures/IAgentTokenUsageInput"; +export * from "./structures/IAgentTokenUsageOutput"; + +// responses — the streamed turn +export * from "./responses/IAgentExecution"; +export * from "./responses/IAgentResponse"; +export * from "./responses/IAgentText"; +export * from "./responses/IAgentTool"; +export * from "./responses/IAgentFeedbackProps"; +export * from "./responses/IAgentValidation"; +export * from "./responses/IAgentValidationState"; +export * from "./responses/IAgentExecute"; +export * from "./responses/IAgentTurn"; + +// histories — memory +export * from "./histories/IAgentHistory"; +export * from "./histories/IAgentHistoryBase"; +export * from "./histories/IAgentTextHistory"; +export * from "./histories/IAgentToolHistory"; +export * from "./histories/IAgentSystemHistory"; +export * from "./histories/IAgentHistoryJson"; +export * from "./histories/IAgentTextHistoryJson"; +export * from "./histories/IAgentToolHistoryJson"; +export * from "./histories/IAgentSystemHistoryJson"; + +// events — telemetry (secondary) +export * from "./events/IAgentEvent"; +export * from "./events/IAgentEventBase"; +export * from "./events/IAgentEventMapper"; +export * from "./events/IAgentEventSource"; +export * from "./events/IAgentRequestEvent"; +export * from "./events/IAgentResponseEvent"; +export * from "./events/IAgentValidateEvent"; +export * from "./events/IAgentParseEvent"; +export * from "./events/IAgentContinuationEvent"; +export * from "./events/IAgentUsageEvent"; diff --git a/packages/agent/src/responses/IAgentExecute.ts b/packages/agent/src/responses/IAgentExecute.ts new file mode 100644 index 00000000000..81d5c898fcb --- /dev/null +++ b/packages/agent/src/responses/IAgentExecute.ts @@ -0,0 +1,34 @@ +import { IAgentOperation } from "../structures/IAgentOperation"; + +/** + * {@link IAgentTool} 실행 기록 — {@link IAgentTool.execute}가 resolve하는 값이자, 모델이 설명하도록 + * history에 기록되는 것. + * + * @author Jeongho Nam - https://github.com/samchon + * @template Arguments 함수에 전달된 validated 인자 타입. + * @template Result 함수의 반환 타입. + */ +export interface IAgentExecute { + /** 발신 {@link IAgentTool.id}와 일치하는 상관 id. */ + id: string; + + /** 어떤 operation이 실행됐는가. */ + operation: IAgentOperation; + + /** 함수가 호출된 validated 인자. */ + arguments: Arguments; + + /** + * 성공 시 함수의 반환값. + * + * `"output"` 프로토콜 operation(structured output, executor 없음)에서는 validated + * payload 자체. + */ + value: Result; + + /** 실행이 throw 없이 끝났는지. */ + success: boolean; + + /** {@link success}가 `false`일 때 던져진 에러. */ + error?: unknown; +} diff --git a/packages/agent/src/responses/IAgentExecution.ts b/packages/agent/src/responses/IAgentExecution.ts new file mode 100644 index 00000000000..699a904beb0 --- /dev/null +++ b/packages/agent/src/responses/IAgentExecution.ts @@ -0,0 +1,50 @@ +import { IAgentResponse } from "./IAgentResponse"; +import { IAgentTurn } from "./IAgentTurn"; + +/** + * {@link IAgent.conversate}가 resolve하는 것: `for await`로 구동하는 단일 턴의 라이브 + * {@link IAgentResponse} part 스트림. + * + * `IAgentExecution`은 그 자체로 async-iterable이다 — stream _이_ 반환값이며, agentica의 + * "`Promise` + 외부 이벤트" 모델을 뒤집는다. 내부 async-iterator 는 part를 yield하고 + * {@link IAgentTurn}을 _return_한다. `for await` 문이 part를 소비하고, 결과는 {@link join}으로 + * 회수한다: + * + * ```ts + * const execution = await agent.conversate("Yaho~"); + * for await (const r of execution) { + * if (r.type === "tool") { ... } + * else if (r.type === "text") { ... } + * } + * const { histories, usage, stop } = await execution.join(); + * ``` + * + * Iterator는 **lazy/pull 주도**다. 매 `next()`가 harness를 전진시키며, 이것이 `tool` part가 + * 소비자의 `execute()`/`feedback()` 결정을 위해 턴 재개 전에 멈출 수 있는 이유다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentExecution extends AsyncIterable { + /** + * Async-iterator: {@link IAgentResponse} part를 **yield**하고 {@link IAgentTurn} + * 결과를 **return**한다. 3-channel 시그니처는 return 값이 턴 결과를 나른다는 것을 문서화한다(평범한 `for + * await`는 버리고, {@link join}이 소비). + */ + [Symbol.asyncIterator](): AsyncIterator; + + /** + * 남은 part를 drain하고 턴 결과를 resolve한다. + * + * 순회 없이 호출해도(턴을 끝까지 실행) 안전하고, `for await` 이후 호출하면 이미 계산된 결과로 즉시 resolve한다. + */ + join(): Promise; + + /** + * 턴을 중단한다: adapter 스트림을 취소하고, 대기 중 `execute()`를 reject하며, iterator를 `stop: + * "aborted"`로 settle한다. + */ + abort(reason?: unknown): void; + + /** {@link abort} 호출 또는 입력 signal 발화 시 abort되는 signal. */ + readonly signal: AbortSignal; +} diff --git a/packages/agent/src/responses/IAgentFeedbackProps.ts b/packages/agent/src/responses/IAgentFeedbackProps.ts new file mode 100644 index 00000000000..6c50373e77c --- /dev/null +++ b/packages/agent/src/responses/IAgentFeedbackProps.ts @@ -0,0 +1,18 @@ +import { IValidation } from "@typia/interface"; + +/** + * {@link IAgentTool.feedback} correction 라운드를 위한 추가 입력. + * + * @author Jeongho Nam - https://github.com/samchon + * @template Arguments 교정 대상 함수의 인자 타입. + */ +export interface IAgentFeedbackProps { + /** Correction prompt에 덧붙일 사람이 읽을 수 있는 안내(자동 생성 에러 주석 위에). 예: 모델이 위반한 비즈니스 규칙. */ + reason?: string | undefined; + + /** + * 현재 snapshot을 재검증하는 대신 feedback할 사전 계산 validation. 커스텀 validator가 실패를 만들었을 때 + * 유용. + */ + validation?: IValidation | undefined; +} diff --git a/packages/agent/src/responses/IAgentResponse.ts b/packages/agent/src/responses/IAgentResponse.ts new file mode 100644 index 00000000000..234ddb6946c --- /dev/null +++ b/packages/agent/src/responses/IAgentResponse.ts @@ -0,0 +1,24 @@ +import { IAgentText } from "./IAgentText"; +import { IAgentTool } from "./IAgentTool"; + +/** + * Agent의 streamed 턴의 한 part — `for await`로 순회하는 요소 타입. mandate의 루프가 읽는 그대로 + * `type`으로 판별되는 discriminated union이다: + * + * ```ts + * for await (const r of await agent.conversate("Yaho~")) { + * if (r.type === "tool") { ... } // IAgentTool + * else if (r.type === "text") { ... } // IAgentText + * } + * ``` + * + * 이 union은 **additive**하다. 향후 멤버(`"reasoning"`, `"file"`, `"source"`, + * `"error"`)가 두 핵심 분기를 건드리지 않고 합류할 수 있다. + * + * @author Jeongho Nam - https://github.com/samchon + * @template Arguments Tool part 인자 타입. + * @template Result Tool part 반환 타입. + */ +export type IAgentResponse = + | IAgentText + | IAgentTool; diff --git a/packages/agent/src/responses/IAgentText.ts b/packages/agent/src/responses/IAgentText.ts new file mode 100644 index 00000000000..26303f797d8 --- /dev/null +++ b/packages/agent/src/responses/IAgentText.ts @@ -0,0 +1,49 @@ +/** + * Agent 턴의 자유 서술 텍스트 part — {@link IAgentResponse}의 `r.type === "text"` 분기. + * + * Streaming 산문 채널이다. assistant 답변이거나, tool 결과 설명("describe")이다. {@link stream}으로 + * token 단위 소비하거나 {@link join}으로 전체를 await한다: + * + * ```ts + * for await (const r of execution) + * if (r.type === "text") + * for await (const piece of r.stream()) process.stdout.write(piece); + * ``` + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentText { + /** 판별자(discriminator). */ + type: "text"; + + /** 턴 내 이 part의 안정 id(event/telemetry 상관용). */ + id: string; + + /** 항상 assistant. 사용자 텍스트는 입력이지 response part가 아니다. */ + role: "assistant"; + + /** + * 이 텍스트가 존재하는 이유. + * + * - `"answer"` — 사용자에 대한 모델의 직접 응답. + * - `"describe"` — 하나 이상의 tool 결과에 대한 자연어 설명(agentica의 "describe" 단계). 같은 턴에서 + * 실행 이후에 나온다. + */ + purpose: "answer" | "describe"; + + /** + * 도착하는 대로 텍스트 token 조각을 streaming한다. + * + * 각 yield는 증분 delta(누적 텍스트 아님)다. part가 끝나면 iterable이 완료된다. 일찍 중단해도 안전하며 + * harness가 정리한다. + */ + stream(): AsyncIterable; + + /** + * 완성된 텍스트를 await한다. + * + * 내부적으로 {@link stream}을 drain하여 이어붙인 결과를 resolve한다. `stream()`과 `join()`을 함께 + * 호출해도 같은 버퍼를 공유하므로 안전하다. + */ + join(): Promise; +} diff --git a/packages/agent/src/responses/IAgentTool.ts b/packages/agent/src/responses/IAgentTool.ts new file mode 100644 index 00000000000..f31c1fd9fcb --- /dev/null +++ b/packages/agent/src/responses/IAgentTool.ts @@ -0,0 +1,82 @@ +import { IAgentOperation } from "../structures/IAgentOperation"; +import { IAgentExecute } from "./IAgentExecute"; +import { IAgentFeedbackProps } from "./IAgentFeedbackProps"; +import { IAgentValidation } from "./IAgentValidation"; + +/** + * Agent 턴의 함수 호출 part — {@link IAgentResponse}의 `r.type === "tool"` 분기이자 harness + * 전체의 쇼케이스. + * + * Native `tool_calls`(불투명한 인자 덩어리 하나)와 달리, `IAgentTool`은 텍스트로 streaming되어 들어와 + * lenient parsing으로 복구된 structured payload 위에서 도는 _소비자 주도 state machine_이다. + * {@link IAgentValidation}을 상속하므로 인자가 _incremental하게_ 관찰·검증되며, mandate의 루프가 필요로 + * 하는 세 행동 — 점검, 교정, 실행 — 을 더한다: + * + * ```ts + * for await (const r of execution) + * if (r.type === "tool") { + * if ((await r.success()) === false) + * await r.feedback(); // 교정 + * else await r.execute(); // 실행 + * } + * ``` + * + * 실행은 **소비자에 의해 게이팅**된다. harness는 validated 호출을 노출하고, 호출자가 실행/교정/건너뜀을 + * 결정한다(permission / human-in-the-loop 점검의 자연스러운 자리). 이 part를 지나 iterator를 진행시키면 + * 턴이 재개되어 소비자가 한 일을 feedback하며, orchestrator의 후속 설명은 이후 {@link IAgentText} part로 + * 도착한다. + * + * @author Jeongho Nam - https://github.com/samchon + * @template Arguments 함수의 parameter 타입(바인딩 전엔 `unknown`). + * @template Result 함수의 반환 타입. + */ +export interface IAgentTool< + Arguments = unknown, + Result = unknown, +> extends IAgentValidation { + /** 판별자(discriminator). */ + type: "tool"; + + /** 턴 내 이 호출의 안정 id. */ + id: string; + + /** 모델이 호출하려는 함수(이름, schema, protocol, executor). */ + operation: IAgentOperation; + + /** + * 도착하는 대로 raw 인자-텍스트 delta를 streaming한다 — {@link IAgentText.stream}의 대칭 동반자. + * _파싱·검증된_ partial은 {@link IAgentValidation.watch}를 선호하라. + */ + stream(): AsyncIterable; + + /** + * Correction feedback-loop를 돌리고 교정된 호출로 resolve한다. + * + * Validation 에러를 annotated JSON(`LlmJson.stringify`, harness의 Tier-2 + * feedback) 으로 렌더해 모델에 재요청하며 {@link IAgentConfig.retry}로 제한된다. 모델의 교정 인자를 담은 + * _새_ {@link IAgentTool}을 반환하여 조합 가능하다: + * + * ```ts + * let tool = r; + * while ((await tool.success()) === false) tool = await tool.feedback(); + * await tool.execute(); + * ``` + * + * @param props Correction prompt에 포함할 추가 안내(선택). + */ + feedback( + props?: IAgentFeedbackProps, + ): Promise>; + + /** + * Validated 인자로 바인딩된 함수를 실행한다. + * + * {@link IAgentExecute} 실행 기록으로 resolve하며 모델의 후속을 위해 history에 기록한다. `"output"` + * 프로토콜 operation(structured output, executor 없음)에서는 resolve된 `value`가 + * validated payload 자체다. + * + * 인자가 validate되기 전에 호출하면 throw한다. {@link success}로 가드하거나 먼저 {@link feedback}을 + * 쓰라. + */ + execute(): Promise>; +} diff --git a/packages/agent/src/responses/IAgentTurn.ts b/packages/agent/src/responses/IAgentTurn.ts new file mode 100644 index 00000000000..4d952572c69 --- /dev/null +++ b/packages/agent/src/responses/IAgentTurn.ts @@ -0,0 +1,34 @@ +import { IAgentHistory } from "../histories/IAgentHistory"; +import { IAgentTokenUsage } from "../structures/IAgentTokenUsage"; + +/** + * 한 번의 {@link IAgent.conversate} 턴 결과 — {@link IAgentExecution}의 async-iterator 가 + * (yield하는 part가 아니라) _return_하는 값이자, {@link IAgentExecution.join}으로도 닿는 값. + * + * "part의 stream"과 "단일 종료 결과"의 분리는 Claude Code의 `AsyncGenerator` 형태를 따른다. `for await`가 part를 소비하고, 결과는 턴이 왜 끝났고 무엇을 영속화할지를 알려준다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentTurn { + /** + * 이 턴에 생성된 새 history(사용자 입력, assistant 텍스트, tool 호출+결과). store에 append하라. + * {@link IAgentHistoryJson}로 직렬화하면 {@link IAgentProps.histories}로 나중에 재개할 수 + * 있다. + */ + histories: IAgentHistory[]; + + /** 이 턴의 토큰 usage({@link IAgentTokenUsage.continuations} 포함). */ + usage: IAgentTokenUsage; + + /** + * 턴이 끝난 이유: + * + * - `"completed"` — 모델이 더 할 말 없이 끝남. + * - `"aborted"` — {@link IAgentExecution.abort}(또는 abort signal)가 발화. + * - `"ceiling"` — 구조 완성 전에 continuation {@link IAgentContinuation.limit}에 + * 도달(truncated output). + * - `"error"` — 치명적 adapter/transport 에러로 턴 종료(상세는 `error` 이벤트). + */ + stop: "completed" | "aborted" | "ceiling" | "error"; +} diff --git a/packages/agent/src/responses/IAgentValidation.ts b/packages/agent/src/responses/IAgentValidation.ts new file mode 100644 index 00000000000..c9b911718d4 --- /dev/null +++ b/packages/agent/src/responses/IAgentValidation.ts @@ -0,0 +1,54 @@ +import { DeepPartial, IValidation } from "@typia/interface"; + +import { IAgentValidationState } from "./IAgentValidationState"; + +/** + * **Incremental validation** 표면 — `@typia/agent`의 심장. 모든 streamed structured + * payload({@link IAgentTool}의 arguments, structured-output 답변)가 공유한다. + * + * 통상적 function calling이 `generate-all → validate-once`인 반면, 이 인터페이스는 harness의 + * 뒤집힌 루프 — `generate → parse partial → validate prefix → lock → continue` — 를 + * 소비 가능한 view로 노출한다: + * + * - {@link snapshot}: 누적 token prefix로부터 typia lenient parser가 복구한 최신 + * `DeepPartial`(닫히지 않은 괄호, truncation, double-stringified union 등을 복구). + * - {@link locked}: 마지막으로 _validated_된 checkpoint. 외부 상태다. output-token 천장이 스트림을 + * 끊어도 locked prefix는 **버려지지 않고** harness가 그로부터 새 completion을 이어간다. + * - {@link watch}: `{ snapshot, validation, phase }` 업데이트를 streaming하여 구조가 채워지는 대로 + * 렌더/반응 가능 — 다른 SDK가 내놓는 미검증 whole-snapshot이 아니라 typed _이며 validated_된 + * partial. + * - {@link validate} / {@link success}: streaming(continuation 포함)이 끝나면 resolve. + * + * @author Jeongho Nam - https://github.com/samchon + * @template T Streaming 중인 완성형 타입. + */ +export interface IAgentValidation { + /** 최신 복구 partial 값(매 parse cadence tick마다 커질 수 있음). */ + snapshot(): DeepPartial; + + /** + * 마지막 validated checkpoint — token 천장 절단을 견디고 continuation을 seed하는 prefix. 첫 + * prefix가 validate되기 전엔 비어 있다. + */ + locked(): DeepPartial; + + /** + * 구조가 채워지는 과정을 증분으로 관찰한다. + * + * 값이 안정될 때까지 매 parse/validate tick마다 새 {@link IAgentValidationState}를 내보낸다. + * 점진적 UI에 이상적. + */ + watch(): AsyncIterable>; + + /** + * 최종 복구 값을 validate한다. + * + * Streaming(continuation 포함)이 끝나길 기다린 뒤 typia 생성 validator를 돌린다. 반환될 수 있는 + * `IValidation.IFailure`는 정밀한 `path`/`expected`/`value` 에러를 담아 + * {@link IAgentTool.feedback}에 곧바로 쓸 수 있다. + */ + validate(): Promise>; + + /** 편의: `(await validate()).success`. */ + success(): Promise; +} diff --git a/packages/agent/src/responses/IAgentValidationState.ts b/packages/agent/src/responses/IAgentValidationState.ts new file mode 100644 index 00000000000..6b64dddb597 --- /dev/null +++ b/packages/agent/src/responses/IAgentValidationState.ts @@ -0,0 +1,32 @@ +import { DeepPartial, IValidation } from "@typia/interface"; + +/** + * {@link IAgentValidation.watch}가 내보내는 하나의 증분 업데이트. + * + * {@link phase}(BAML의 `StreamState`에서 가져옴)는 소비자가 _아직 streaming 중_ / _validated_ + * / _locked_ / _failed_를 구별하게 한다 — 평평한 `DeepPartial` 스냅샷에는 없는 semantic 상태 + * 신호. + * + * @author Jeongho Nam - https://github.com/samchon + * @template T Streaming 중인 완성형 타입. + */ +export interface IAgentValidationState { + /** + * 이 스냅샷의 lifecycle 위치: + * + * - `"streaming"` — 토큰이 더 온다. 스냅샷은 잠정. + * - `"validated"` — 현재 prefix가 validation 통과(lock 후보). + * - `"locked"` — prefix가 checkpoint됨. continuation에도 안전. + * - `"failed"` — 현재 prefix가 스키마 위반(feedback 후보). + */ + phase: "streaming" | "validated" | "locked" | "failed"; + + /** 이 시점의 복구된 partial 값. */ + snapshot: DeepPartial; + + /** 이번 tick에 harness가 validation을 돌렸을 때의 결과. tick 사이 단순 streaming 중에는 `null`. */ + validation: IValidation | null; + + /** 직전 tick 이후 추가된 raw 텍스트(echo/logging용). */ + delta?: string | undefined; +} diff --git a/packages/agent/src/structures/IAgentAdapter.ts b/packages/agent/src/structures/IAgentAdapter.ts new file mode 100644 index 00000000000..a6eec99736f --- /dev/null +++ b/packages/agent/src/structures/IAgentAdapter.ts @@ -0,0 +1,46 @@ +import { IAgentAdapterCapabilities } from "./IAgentAdapterCapabilities"; +import { IAgentAdapterRequest } from "./IAgentAdapterRequest"; +import { IAgentChunk } from "./IAgentChunk"; + +/** + * 벤더 중립 LLM streaming adapter — `@typia/agent`를 provider 비종속으로 만드는 단 하나의 + * 시임(seam). + * + * 어떤 chat-completion backend(OpenAI SDK, Vercel AI SDK, self-hosted 모델로의 raw + * `fetch` 등)든 하나의 primitive로 환원한다. 즉 요청을 받아 {@link IAgentChunk}의 async 스트림을 + * 돌려준다. Vercel의 `LanguageModelV2.doStream`을 본떴으되, harness가 native `tool_calls`가 + * 아니라 _structured 텍스트_를 streaming하므로 chunk union이 의도적으로 **더 작다**. native + * tool-call streaming은 선택적 capability이지 핵심 경로가 아니다. + * + * Provider 고유의 모든 것 — OpenAI의 "첫 tool-call delta에만 id/name이 온다", + * `stream_options.include_usage`, Vercel의 major 버전 간 식별자 변동 — 은 adapter + * **내부에서** 정규화된다. 위의 harness는 그것을 결코 보지 않는다. typia의 {@link ILlmSchema}가 vendor + * 통합형(모델별 schema 변형 없음)이라, adapter가 provider 지식이 사는 _유일한_ 장소다. + * + * 라이브러리는 이 인터페이스의 구현체(`OpenAiAdapter`, `VercelAdapter` 등)를 제공한다. provider 교체는 + * {@link IAgentProps.adapter} 한 줄 교체이며 agent 코드는 그대로다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentAdapter { + /** 로깅/telemetry용 provider 식별자(예: `"openai"`, `"vercel"`). */ + readonly provider: string; + + /** Backend에 전달되는 모델 식별자(예: `"gpt-4o"`, `"claude-..."`). */ + readonly model: string; + + /** + * 선택적 capability 광고. + * + * 없으면 text-streaming만 가능하다고 가정한다. + */ + readonly capabilities?: IAgentAdapterCapabilities | undefined; + + /** + * Streaming completion을 연다. + * + * @param input 중립 요청(messages + 선택적 schema/tool 힌트). + * @returns 중립 {@link IAgentChunk}의 async iterable. + */ + stream(input: IAgentAdapterRequest): AsyncIterable; +} diff --git a/packages/agent/src/structures/IAgentAdapterCapabilities.ts b/packages/agent/src/structures/IAgentAdapterCapabilities.ts new file mode 100644 index 00000000000..24a2f50d4e6 --- /dev/null +++ b/packages/agent/src/structures/IAgentAdapterCapabilities.ts @@ -0,0 +1,18 @@ +/** + * {@link IAgentAdapter}가 광고하는 선택적 backend capability. + * + * Harness가 더 빠른 native 경로(예: native structured output, prompt caching)를 쓸 수 + * 있는지를 알려준다. 없으면 text-streaming만 가능하다고 가정한다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentAdapterCapabilities { + /** Backend가 JSON schema를 native로 강제할 수 있는가(OpenAI structured output). */ + structuredOutput?: boolean | undefined; + + /** Backend가 native `tools` / `tool_calls`를 지원하는가. */ + nativeTools?: boolean | undefined; + + /** Backend가 prompt caching을 지원하는가(history/compaction 전략에 영향). */ + promptCache?: boolean | undefined; +} diff --git a/packages/agent/src/structures/IAgentAdapterRequest.ts b/packages/agent/src/structures/IAgentAdapterRequest.ts new file mode 100644 index 00000000000..c64026238fb --- /dev/null +++ b/packages/agent/src/structures/IAgentAdapterRequest.ts @@ -0,0 +1,31 @@ +import { ILlmSchema } from "@typia/interface"; + +import { IAgentMessage } from "./IAgentMessage"; + +/** + * {@link IAgentAdapter}에 보내는 중립 요청. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentAdapterRequest { + /** 지금까지의 대화를 중립 메시지 형태로. */ + messages: IAgentMessage[]; + + /** + * 선택적 structured-output 힌트. + * + * Harness가 backend의 _native_ structured output을 원하고 adapter가 + * {@link IAgentAdapterCapabilities.structuredOutput}을 광고할 때 parameter schema를 + * 여기에 넘긴다. 그 외에는 harness가 그냥 텍스트를 streaming해 직접 파싱한다 — 이 라이브러리의 핵심. + */ + response?: ILlmSchema.IParameters | undefined; + + /** Structured output 생성을 얼마나 강하게 유도할지(지원 시). */ + toolChoice?: "auto" | "required" | "none" | undefined; + + /** {@link IAgentExecution.abort}에서 연결된 abort 핸들. */ + abortSignal?: AbortSignal | undefined; + + /** Provider 고유 escape hatch(sampling 파라미터, 헤더 등). */ + options?: Record | undefined; +} diff --git a/packages/agent/src/structures/IAgentAudioContent.ts b/packages/agent/src/structures/IAgentAudioContent.ts new file mode 100644 index 00000000000..9e9a2949839 --- /dev/null +++ b/packages/agent/src/structures/IAgentAudioContent.ts @@ -0,0 +1,15 @@ +/** + * 멀티모달 메시지의 오디오 content part. base64 인코딩된 클립. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentAudioContent { + /** 판별자(discriminator). */ + type: "audio"; + + /** Base64 인코딩된 오디오 데이터. */ + data: string; + + /** 오디오 포맷. */ + format: "wav" | "mp3" | (string & {}); +} diff --git a/packages/agent/src/structures/IAgentChunk.ts b/packages/agent/src/structures/IAgentChunk.ts new file mode 100644 index 00000000000..4ae4499ddcd --- /dev/null +++ b/packages/agent/src/structures/IAgentChunk.ts @@ -0,0 +1,18 @@ +import { IAgentErrorChunk } from "./IAgentErrorChunk"; +import { IAgentFinishChunk } from "./IAgentFinishChunk"; +import { IAgentRawChunk } from "./IAgentRawChunk"; +import { IAgentTextDeltaChunk } from "./IAgentTextDeltaChunk"; + +/** + * {@link IAgentAdapter}가 내보내는 한 개의 streamed 이벤트. `type`으로 판별된다. + * + * 일부러 최소로 유지한다. harness가 _텍스트_를 streaming하므로 union은 `text-delta` + `finish`가 + * 지배하며, 매끄럽게 매핑되지 않는 provider 고유 이벤트는 최저공통분모 함정 대신 `raw`로 흘려보낸다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export type IAgentChunk = + | IAgentTextDeltaChunk + | IAgentFinishChunk + | IAgentErrorChunk + | IAgentRawChunk; diff --git a/packages/agent/src/structures/IAgentClassController.ts b/packages/agent/src/structures/IAgentClassController.ts new file mode 100644 index 00000000000..07e2dbfd7dc --- /dev/null +++ b/packages/agent/src/structures/IAgentClassController.ts @@ -0,0 +1,30 @@ +import { ILlmApplication } from "@typia/interface"; + +import { IAgentClassExecuteProps } from "./IAgentClassExecuteProps"; + +/** + * TypeScript class-method controller — `typia.llm.application()`로 만든 + * function 스키마와, 그 메서드가 도는 인스턴스(또는 dispatch 콜백)를 묶는다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentClassController { + /** 프로토콜 판별자. */ + protocol: "class"; + + /** Controller 식별자(함수명 중복 제거 시 namespace, operation에 노출). */ + name: string; + + /** `typia.llm.application()`로 생성한 function 스키마. */ + application: ILlmApplication; + + /** + * 실행 대상. + * + * 메서드가 호출되는 class 인스턴스이거나, `(name, arguments)`를 값으로 dispatch 하는 콜백이다. 후자는 + * proxy와 원격 실행을 가능하게 한다. + */ + execute: + | Class + | ((next: IAgentClassExecuteProps) => Promise | unknown); +} diff --git a/packages/agent/src/structures/IAgentClassExecuteProps.ts b/packages/agent/src/structures/IAgentClassExecuteProps.ts new file mode 100644 index 00000000000..ee7bab460ab --- /dev/null +++ b/packages/agent/src/structures/IAgentClassExecuteProps.ts @@ -0,0 +1,18 @@ +import { ILlmApplication } from "@typia/interface"; + +/** + * 콜백 형태의 class controller 실행기({@link IAgentClassController.execute})에 전달되는 인자 + * 묶음. proxy / 원격 실행을 가능하게 한다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentClassExecuteProps { + /** 대상 controller의 function calling application. */ + application: ILlmApplication; + + /** 호출 대상 함수의 schema. */ + function: ILlmApplication["functions"][number]; + + /** 모델이 만든 인자. */ + arguments: Record; +} diff --git a/packages/agent/src/structures/IAgentClassOperation.ts b/packages/agent/src/structures/IAgentClassOperation.ts new file mode 100644 index 00000000000..e8526a05240 --- /dev/null +++ b/packages/agent/src/structures/IAgentClassOperation.ts @@ -0,0 +1,11 @@ +import { IAgentOperationBase } from "./IAgentOperationBase"; + +/** + * Class 메서드로 뒷받침되는 operation. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentClassOperation extends IAgentOperationBase<"class"> { + /** 메서드가 도는 인스턴스 또는 dispatch 콜백. */ + execute: object | ((...args: any[]) => unknown); +} diff --git a/packages/agent/src/structures/IAgentCompaction.ts b/packages/agent/src/structures/IAgentCompaction.ts new file mode 100644 index 00000000000..2c276fd9c95 --- /dev/null +++ b/packages/agent/src/structures/IAgentCompaction.ts @@ -0,0 +1,13 @@ +/** + * 긴 호흡의 대화에서 history를 compaction(summarize-and-truncate)하기 위한 예약 정책. 없으면 매 턴 전체 + * history를 재생한다(agentica 패리티). + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentCompaction { + /** 재생되는 history가 이 토큰 추정치를 넘으면 compaction을 trigger한다. */ + threshold?: number | undefined; + + /** 커스텀 summarizer. 없으면 기본 summarize-and-truncate. */ + summarize?: ((next: unknown) => Promise) | undefined; +} diff --git a/packages/agent/src/structures/IAgentConfig.ts b/packages/agent/src/structures/IAgentConfig.ts new file mode 100644 index 00000000000..64efb3a0e74 --- /dev/null +++ b/packages/agent/src/structures/IAgentConfig.ts @@ -0,0 +1,71 @@ +import { IAgentCompaction } from "./IAgentCompaction"; +import { IAgentContinuation } from "./IAgentContinuation"; +import { IAgentSelector } from "./IAgentSelector"; +import { IAgentSystemPrompt } from "./IAgentSystemPrompt"; + +/** + * {@link IAgent} 튜닝. 모든 필드는 선택이며, 기본값은 `MicroAgentica` 스타일 agent를 재현한다(모든 함수 나열, + * 텍스트 streaming, validate, retry). + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentConfig { + /** Agent가 답하는 BCP-47 locale(예: `"en-US"`, `"ko-KR"`). system prompt에 주입. */ + locale?: string | undefined; + + /** System prompt에 주입되는 IANA timezone(예: `"Asia/Seoul"`). */ + timezone?: string | undefined; + + /** Harness의 내장 system prompt 단계별 override. */ + systemPrompt?: Partial | undefined; + + /** + * Tool 호출당 최대 feedback-correction 시도 횟수. + * + * Parse/validation 실패 시 harness는 annotated 에러(`LlmJson.stringify`)와 함께 모델에 + * 재요청한다. `retry`번 실패하면 포기하고 그 실패를 part에 노출한다. 기본 `3`. + */ + retry?: number | undefined; + + /** + * {@link selector}가 개입하는 함수 수 임계값. + * + * 이하에서는 모든 함수를 모델에 나열(micro 모드). 초과하면 selection/divide-and- conquer가 집합을 좁힌다. + * 기본: 무제한(micro 모드). + */ + capacity?: number | undefined; + + /** 커스텀 대규모 함수 수 전략. 없으면 전부 나열(micro 모드). */ + selector?: IAgentSelector | undefined; + + /** Output-token 천장 정책 — 라이브러리의 핵심 동작. */ + continuation?: IAgentContinuation | undefined; + + /** + * 소비자가 해소(`execute()`/`feedback()`)하지 않고 지나친 {@link IAgentTool} part 처리 정책. + * + * - `"skip"`(기본) — "미실행" 노트를 feedback하고 계속. + * - `"execute"` — validated 인자로 자동 실행. + * - `"throw"` — 프로그래밍 오류를 크게 드러내기 위해 raise. + */ + onUnhandledTool?: "skip" | "execute" | "throw" | undefined; + + /** + * 한 턴 내 독립 tool 호출의 동시 실행 허용 여부. + * + * 기본 `false`(직렬, 단순 `for await` 루프에 부합). `true`면 concurrency-safe operation을 + * 병렬 실행하되 part _emit_ 순서는 보존한다(Claude Code 방식). + */ + parallel?: boolean | undefined; + + /** + * Streaming prefix를 lenient-parse·validate하는 주기. + * + * 매 token마다 parse하면 O(n²)이라 harness는 debounce한다. token 수, ms 간격, 또는 + * `"boundary"`(구조 경계 — newline/닫는 괄호 — 에서 parse) 중 하나로 표현. 기본 `"boundary"`. + */ + validateCadence?: number | "boundary" | undefined; + + /** 긴 호흡에서 history compaction을 위한 예약 hook. 없으면 전체 history 재생. */ + compaction?: IAgentCompaction | undefined; +} diff --git a/packages/agent/src/structures/IAgentContinuation.ts b/packages/agent/src/structures/IAgentContinuation.ts new file mode 100644 index 00000000000..b7f3b0cd95a --- /dev/null +++ b/packages/agent/src/structures/IAgentContinuation.ts @@ -0,0 +1,26 @@ +/** + * Output-token 천장 continuation 정책 — 이 라이브러리의 핵심 동작. + * + * Completion이 구조 중간에 `length` finish로 끝나면 harness는 마지막 locked checkpoint로부터 이어 + * 붙인다. 이 타입이 그 continuation을 제한·조형한다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentContinuation { + /** + * `length` finish를 넘어 이어 붙일지 여부. 기본 `true` — 이게 feature다. `false`로 두면 + * truncation에서 즉시 실패한다. + */ + enabled?: boolean | undefined; + + /** 하나의 논리적 답변에 대한 최대 continuation 횟수. 기본 `8`. */ + limit?: number | undefined; + + /** + * 다음 completion을 locked prefix로부터 어떻게 seed할지. + * + * - `"prefill"`(기본) — locked 텍스트로 assistant 턴을 이어가게 하고 계속 쓰게 한다. + * - `"remainder"` — locked partial을 맥락으로 주고 아직 안 채운 필드만 다시 요청한다. + */ + strategy?: "prefill" | "remainder" | undefined; +} diff --git a/packages/agent/src/structures/IAgentController.ts b/packages/agent/src/structures/IAgentController.ts new file mode 100644 index 00000000000..b9a22955618 --- /dev/null +++ b/packages/agent/src/structures/IAgentController.ts @@ -0,0 +1,22 @@ +import { IAgentClassController } from "./IAgentClassController"; +import { IAgentHttpController } from "./IAgentHttpController"; +import { IAgentMcpController } from "./IAgentMcpController"; + +/** + * Agent에 등록하는 함수 제공자 — {@link IAgentProps.controllers}에 넘기는 단위. + * + * Agentica가 검증한 `protocol` 판별 설계를 그대로 가져왔다. controller는 이름과 + * {@link ILlmApplication}/{@link IHttpLlmApplication}(즉 function 스키마)과 그것을 _실행_할 + * 수단을 묶는다. harness는 매 턴 전에 모든 controller의 함수를 {@link IAgentOperation}으로 평탄화하고 충돌 + * 이름을 중복 제거한다. + * + * - `class` — TypeScript class 메서드(`typia.llm.controller()`); 인스턴스에서 실행. + * - `http` — OpenAPI 문서로부터 만든 REST 백엔드 전체(`HttpLlm.application`); HTTP로 실행. + * - `mcp` — Model Context Protocol 서버; client로 실행. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export type IAgentController = + | IAgentClassController + | IAgentHttpController + | IAgentMcpController; diff --git a/packages/agent/src/structures/IAgentConversateOptions.ts b/packages/agent/src/structures/IAgentConversateOptions.ts new file mode 100644 index 00000000000..fd70f0423be --- /dev/null +++ b/packages/agent/src/structures/IAgentConversateOptions.ts @@ -0,0 +1,9 @@ +/** + * {@link IAgent.conversate}의 턴 단위 옵션. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentConversateOptions { + /** 외부에서 턴을 중단한다. {@link IAgentExecution.signal}에 연결된다. */ + abortSignal?: AbortSignal | undefined; +} diff --git a/packages/agent/src/structures/IAgentErrorChunk.ts b/packages/agent/src/structures/IAgentErrorChunk.ts new file mode 100644 index 00000000000..a645e936c97 --- /dev/null +++ b/packages/agent/src/structures/IAgentErrorChunk.ts @@ -0,0 +1,12 @@ +/** + * Adapter 스트림의 transport/provider 에러 chunk. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentErrorChunk { + /** 판별자(discriminator). */ + type: "error"; + + /** 에러 객체. */ + error: unknown; +} diff --git a/packages/agent/src/structures/IAgentFileContent.ts b/packages/agent/src/structures/IAgentFileContent.ts new file mode 100644 index 00000000000..ea230cffc00 --- /dev/null +++ b/packages/agent/src/structures/IAgentFileContent.ts @@ -0,0 +1,21 @@ +/** + * 멀티모달 메시지의 문서/파일 content part. URL 또는 인라인 데이터로 표현한다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentFileContent { + /** 판별자(discriminator). */ + type: "file"; + + /** 파일 URL. */ + url?: string | undefined; + + /** Base64 인코딩된 인라인 데이터. */ + data?: string | undefined; + + /** 파일명. */ + name?: string | undefined; + + /** MIME 타입. */ + mediaType?: string | undefined; +} diff --git a/packages/agent/src/structures/IAgentFinishChunk.ts b/packages/agent/src/structures/IAgentFinishChunk.ts new file mode 100644 index 00000000000..3868d121daa --- /dev/null +++ b/packages/agent/src/structures/IAgentFinishChunk.ts @@ -0,0 +1,18 @@ +import { IAgentFinishReason } from "./IAgentFinishReason"; +import { IAgentTokenUsage } from "./IAgentTokenUsage"; + +/** + * Stop 사유와 보고된 usage를 담은 종료 chunk. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentFinishChunk { + /** 판별자(discriminator). */ + type: "finish"; + + /** Completion이 끝난 이유. */ + finishReason: IAgentFinishReason; + + /** Provider가 보고한 토큰 usage. */ + usage: IAgentTokenUsage; +} diff --git a/packages/agent/src/structures/IAgentFinishReason.ts b/packages/agent/src/structures/IAgentFinishReason.ts new file mode 100644 index 00000000000..f136cfe7b02 --- /dev/null +++ b/packages/agent/src/structures/IAgentFinishReason.ts @@ -0,0 +1,15 @@ +/** + * Completion이 끝난 이유. + * + * `"length"`는 harness가 **output-token 천장을 넘어** locked checkpoint로부터 이어 붙이기 위해 + * 사용하는 신호다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export type IAgentFinishReason = + | "stop" + | "length" + | "content-filter" + | "abort" + | "error" + | "other"; diff --git a/packages/agent/src/structures/IAgentHttpController.ts b/packages/agent/src/structures/IAgentHttpController.ts new file mode 100644 index 00000000000..66498f6248e --- /dev/null +++ b/packages/agent/src/structures/IAgentHttpController.ts @@ -0,0 +1,28 @@ +import { IHttpConnection, IHttpLlmApplication } from "@typia/interface"; + +import { IAgentHttpExecuteProps } from "./IAgentHttpExecuteProps"; + +/** + * OpenAPI/HTTP backend controller — REST 백엔드 전체를 그 OpenAPI 문서 + * (`HttpLlm.application`)로부터 function으로 바꾼다. HTTP 요청을 보내 실행한다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentHttpController { + /** 프로토콜 판별자. */ + protocol: "http"; + + /** Controller 식별자. */ + name: string; + + /** `HttpLlm.application({ document })`로 생성한 function 스키마. */ + application: IHttpLlmApplication; + + /** 실제 요청을 보낼 connection(host + headers). */ + connection: IHttpConnection; + + /** 기본 fetch 기반 실행기를 덮어쓰는 선택적 override. */ + execute?: + | ((next: IAgentHttpExecuteProps) => Promise | unknown) + | undefined; +} diff --git a/packages/agent/src/structures/IAgentHttpExecuteProps.ts b/packages/agent/src/structures/IAgentHttpExecuteProps.ts new file mode 100644 index 00000000000..402bf3b6d70 --- /dev/null +++ b/packages/agent/src/structures/IAgentHttpExecuteProps.ts @@ -0,0 +1,20 @@ +import { IHttpConnection, IHttpLlmApplication } from "@typia/interface"; + +/** + * 커스텀 HTTP 실행기({@link IAgentHttpController.execute})에 전달되는 인자 묶음. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentHttpExecuteProps { + /** 대상 controller의 HTTP function calling application. */ + application: IHttpLlmApplication; + + /** 호출 대상 함수의 schema. */ + function: IHttpLlmApplication["functions"][number]; + + /** 요청을 보낼 connection(host + headers). */ + connection: IHttpConnection; + + /** 모델이 만든 인자. */ + arguments: Record; +} diff --git a/packages/agent/src/structures/IAgentHttpOperation.ts b/packages/agent/src/structures/IAgentHttpOperation.ts new file mode 100644 index 00000000000..9b759725973 --- /dev/null +++ b/packages/agent/src/structures/IAgentHttpOperation.ts @@ -0,0 +1,16 @@ +import { IHttpConnection, IHttpLlmFunction } from "@typia/interface"; + +import { IAgentOperationBase } from "./IAgentOperationBase"; + +/** + * HTTP 엔드포인트로 뒷받침되는 operation. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentHttpOperation extends IAgentOperationBase<"http"> { + /** HTTP function 스키마(method/path/operation/route 포함). */ + function: IHttpLlmFunction; + + /** 요청을 보낼 connection. */ + connection: IHttpConnection; +} diff --git a/packages/agent/src/structures/IAgentImageContent.ts b/packages/agent/src/structures/IAgentImageContent.ts new file mode 100644 index 00000000000..ef9497e352d --- /dev/null +++ b/packages/agent/src/structures/IAgentImageContent.ts @@ -0,0 +1,15 @@ +/** + * 멀티모달 메시지의 이미지 content part. URL 또는 인라인 데이터로 표현한다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentImageContent { + /** 판별자(discriminator). */ + type: "image"; + + /** 이미지 URL(또는 data URI). */ + url: string; + + /** Vision 모델을 위한 detail 힌트. */ + detail?: "auto" | "low" | "high" | undefined; +} diff --git a/packages/agent/src/structures/IAgentMcpController.ts b/packages/agent/src/structures/IAgentMcpController.ts new file mode 100644 index 00000000000..2d9239702e3 --- /dev/null +++ b/packages/agent/src/structures/IAgentMcpController.ts @@ -0,0 +1,24 @@ +import { ILlmApplication } from "@typia/interface"; + +/** + * Model Context Protocol 서버 controller — MCP 서버의 tool을 그 client로 호출한다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentMcpController { + /** 프로토콜 판별자. */ + protocol: "mcp"; + + /** Controller 식별자. */ + name: string; + + /** MCP 서버의 tool들을 기술하는 function 스키마. */ + application: ILlmApplication; + + /** + * 서버 tool을 호출하는 MCP client. + * + * 타입 표면에 MCP SDK 의존을 두지 않기 위해 `unknown`으로 둔다. 구체 adapter가 좁힌다. + */ + client: unknown; +} diff --git a/packages/agent/src/structures/IAgentMcpOperation.ts b/packages/agent/src/structures/IAgentMcpOperation.ts new file mode 100644 index 00000000000..76c246d1a84 --- /dev/null +++ b/packages/agent/src/structures/IAgentMcpOperation.ts @@ -0,0 +1,11 @@ +import { IAgentOperationBase } from "./IAgentOperationBase"; + +/** + * MCP tool로 뒷받침되는 operation. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentMcpOperation extends IAgentOperationBase<"mcp"> { + /** 서버 tool을 호출하는 MCP client. */ + client: unknown; +} diff --git a/packages/agent/src/structures/IAgentMessage.ts b/packages/agent/src/structures/IAgentMessage.ts new file mode 100644 index 00000000000..49d221c9cf9 --- /dev/null +++ b/packages/agent/src/structures/IAgentMessage.ts @@ -0,0 +1,22 @@ +import { IAgentMessageContent } from "./IAgentMessageContent"; + +/** + * {@link IAgentAdapter}로 전달되는 중립 채팅 메시지. + * + * OpenAI의 `ChatCompletionMessageParam`, Vercel의 `ModelMessage`에 대응하는 provider + * 중립 타입이다. harness는 매 턴 {@link IAgentHistory}로부터 이 배열을 구성하고, adapter가 경계에서 + * backend 고유 형태로 변환한다. vendor 타입을 재노출하지 않고 자체 메시지 타입을 두는 것이, 하나의 agent 코드가 + * adapter를 갈아끼워도 그대로 도는 이유다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentMessage { + /** 화자 역할. */ + role: "system" | "developer" | "user" | "assistant" | "tool"; + + /** 일반 텍스트(string) 또는 멀티모달 part 배열. */ + content: string | IAgentMessageContent[]; + + /** Tool/function 결과를 그 호출에 묶기 위한 상관 id(`role: "tool"`에서 사용). */ + toolCallId?: string | undefined; +} diff --git a/packages/agent/src/structures/IAgentMessageContent.ts b/packages/agent/src/structures/IAgentMessageContent.ts new file mode 100644 index 00000000000..553f5b05637 --- /dev/null +++ b/packages/agent/src/structures/IAgentMessageContent.ts @@ -0,0 +1,20 @@ +import { IAgentAudioContent } from "./IAgentAudioContent"; +import { IAgentFileContent } from "./IAgentFileContent"; +import { IAgentImageContent } from "./IAgentImageContent"; +import { IAgentTextContent } from "./IAgentTextContent"; + +/** + * 멀티모달 {@link IAgentMessage} content 배열의 한 part. + * + * `type`으로 판별되는 discriminated union이다. agentica의 + * `AgenticaUserMessageContent`(`text | image | file | audio`)를 미러링하여 기존 멀티모달 + * 입력이 그대로 이식된다. 이 union은 additive하므로, 모델이 생성하는 미디어(`image`/`file`)도 나중에 + * breaking 없이 합류할 수 있다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export type IAgentMessageContent = + | IAgentTextContent + | IAgentImageContent + | IAgentFileContent + | IAgentAudioContent; diff --git a/packages/agent/src/structures/IAgentOperation.ts b/packages/agent/src/structures/IAgentOperation.ts new file mode 100644 index 00000000000..fd599ec677c --- /dev/null +++ b/packages/agent/src/structures/IAgentOperation.ts @@ -0,0 +1,24 @@ +import { IAgentClassOperation } from "./IAgentClassOperation"; +import { IAgentHttpOperation } from "./IAgentHttpOperation"; +import { IAgentMcpOperation } from "./IAgentMcpOperation"; +import { IAgentOutputOperation } from "./IAgentOutputOperation"; + +/** + * {@link IAgentController}에서 평탄화된 하나의 호출 가능한 함수. + * + * Controller가 함수의 _그룹_이라면, operation은 그것을 실행하는 데 필요한 맥락과 짝지어진 _하나_의 함수다. + * harness는 생성 시 모든 controller를 평탄화하고 충돌 이름을 중복 제거하여 전체 `IAgentOperation[]`을 + * 구성한다(agentica의 `AgenticaOperationComposer`와 동일). selector가 좁히는 대상이자 + * {@link IAgentTool} part가 바인딩되는 대상이다. + * + * `protocol`로 판별된다. 추가된 `"output"` 프로토콜은 executor가 **없는** 순수 structured-output + * 대상으로, structured-output 스토리가 별도의 최상위 response 멤버를 추가하지 않고도 {@link IAgentTool} + * 기계를 공유하게 한다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export type IAgentOperation = + | IAgentClassOperation + | IAgentHttpOperation + | IAgentMcpOperation + | IAgentOutputOperation; diff --git a/packages/agent/src/structures/IAgentOperationBase.ts b/packages/agent/src/structures/IAgentOperationBase.ts new file mode 100644 index 00000000000..7c6771b66f5 --- /dev/null +++ b/packages/agent/src/structures/IAgentOperationBase.ts @@ -0,0 +1,25 @@ +import { ILlmFunction } from "@typia/interface"; + +/** + * 모든 {@link IAgentOperation} 변형이 공유하는 공통 헤드. + * + * @author Jeongho Nam - https://github.com/samchon + * @template Protocol 프로토콜 판별자 리터럴. + */ +export interface IAgentOperationBase { + /** 프로토콜 판별자(출처 controller와 일치). */ + protocol: Protocol; + + /** 출처 controller 이름. */ + controller: string; + + /** + * Agent 내 고유 함수 이름. + * + * Controller 함수명에서 파생되어 controller 간 중복 제거된다. 모델이 이 함수를 호출할 때 내보내는 이름이다. + */ + name: string; + + /** Function calling 스키마(parameters/output/parse/validate/coerce). */ + function: ILlmFunction; +} diff --git a/packages/agent/src/structures/IAgentOutputOperation.ts b/packages/agent/src/structures/IAgentOutputOperation.ts new file mode 100644 index 00000000000..d3a62e030a1 --- /dev/null +++ b/packages/agent/src/structures/IAgentOutputOperation.ts @@ -0,0 +1,12 @@ +import { IAgentOperationBase } from "./IAgentOperationBase"; + +/** + * Executor 없는 structured-output 대상 operation. + * + * Side-effect 함수 호출이 아니라 하나의 큰 typed payload를 생산해야 할 때 사용한다(token-ceiling + * streaming use-case). 이 프로토콜에 바인딩된 {@link IAgentTool}은 incremental validation + * 표면을 모두 노출하되, `execute()`는 validated 값 자체를 resolve한다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentOutputOperation extends IAgentOperationBase<"output"> {} diff --git a/packages/agent/src/structures/IAgentProps.ts b/packages/agent/src/structures/IAgentProps.ts new file mode 100644 index 00000000000..a5eddde182a --- /dev/null +++ b/packages/agent/src/structures/IAgentProps.ts @@ -0,0 +1,39 @@ +import { IAgentHistoryJson } from "../histories/IAgentHistoryJson"; +import { IAgentAdapter } from "./IAgentAdapter"; +import { IAgentConfig } from "./IAgentConfig"; +import { IAgentController } from "./IAgentController"; +import { IAgentTokenUsage } from "./IAgentTokenUsage"; + +/** + * {@link TypiaAgent}(및 모든 {@link IAgent})의 생성자 속성. + * + * 필수는 {@link adapter}뿐이라 가장 작은 agent는 `new TypiaAgent({ adapter })`다. + * {@link controllers}를 더하면 function-calling agent가 되고, {@link histories}를 넘기면 이전 + * 대화를 재개한다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentProps { + /** 벤더 중립 LLM adapter(OpenAI, Vercel 등) — 유일한 필수 필드, provider 교체 한 줄. */ + adapter: IAgentAdapter; + + /** + * Agent가 호출할 수 있는 함수 제공자. 각각 {@link IAgentOperation}으로 평탄화되며 충돌 이름은 중복 제거된다. + * 순수 대화/structured-output agent면 생략. + */ + controllers?: IAgentController[] | undefined; + + /** 동작 튜닝. 생략하면 `MicroAgentica` 등가의 기본값. */ + config?: IAgentConfig | undefined; + + /** + * 재개할 직렬화된 이전 대화. + * + * 이전 턴의 {@link IAgentTurn.histories}로부터 영속화한 {@link IAgentHistoryJson} 배열을 넘기면 + * agent가 live history로 rehydrate한다. + */ + histories?: IAgentHistoryJson[] | undefined; + + /** 누적할 seed 토큰 usage(세션 간 budgeting용). */ + tokenUsage?: IAgentTokenUsage | undefined; +} diff --git a/packages/agent/src/structures/IAgentRawChunk.ts b/packages/agent/src/structures/IAgentRawChunk.ts new file mode 100644 index 00000000000..dd1a166ba17 --- /dev/null +++ b/packages/agent/src/structures/IAgentRawChunk.ts @@ -0,0 +1,14 @@ +/** + * 번역 없이 그대로 통과시키는 provider 고유 이벤트(citations, logprobs 등)의 chunk. + * + * 깔끔히 매핑되지 않는 provider 이벤트를 최저공통분모로 뭉개지 않기 위한 escape hatch다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentRawChunk { + /** 판별자(discriminator). */ + type: "raw"; + + /** Provider 고유 원본 값. */ + value: unknown; +} diff --git a/packages/agent/src/structures/IAgentSelector.ts b/packages/agent/src/structures/IAgentSelector.ts new file mode 100644 index 00000000000..63497b9b4ad --- /dev/null +++ b/packages/agent/src/structures/IAgentSelector.ts @@ -0,0 +1,40 @@ +import { IAgentOperation } from "./IAgentOperation"; +import { IAgentSelectorProps } from "./IAgentSelectorProps"; + +/** + * 함수가 _아주 많을 때_ 턴 전에 함수 집합을 좁히는 전략 — "함수가 매우 많으면?"에 대한 답. + * + * 기본(micro) agent는 selector가 **없다**. 매 턴 모든 operation을 모델에 나열하며, 이는 agentica의 + * `MicroAgentica`와 동일하고 함수 수십 개까지는 이상적이다. {@link IAgentConfig.capacity}를 넘으면 + * `IAgentSelector`가 개입해, 모델이 실제로 보는 operation을 세 가지 조합 가능한 전략으로 사전 선별한다: + * + * 1. **Semantic pre-filter**({@link prefilter}) — 함수 description을 embedding하여 사용자 + * 메시지와 유사한 것만 남긴다. LLM 호출 _이전_, 가장 저렴하고 순수 기계적. + * 2. **Capacity divide-and-conquer** — 생존자를 capacity 크기 그룹으로 쪼개 그룹별로 모델이 후보를 병렬 + * 선택(agentica 방식), union에 대한 elitism 재선택은 선택. + * 3. **Incremental-validation selection** — 선택 자체를 streamed structured output으로 + * 돌린다. 모델이 선택 함수명을 streaming하고 enum/`MinItems` 스키마에 대해 incremental하게 + * validate한다. harness가 제 밥그릇을 먹는 셈. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentSelector { + /** + * 선택적, 비-LLM semantic pre-filter. + * + * 대화와 전체 operation 집합을 받아 (동기/비동기로) 축소된 후보 집합을 돌려준다 (예: embedding 유사도). + * {@link select} 이전에 돈다. + */ + prefilter?: + | (( + props: IAgentSelectorProps, + ) => Promise | IAgentOperation[]) + | undefined; + + /** + * 이번 턴에 모델에 제공할 operation을 고른다. + * + * @returns 후보 operation들(`props.operations`의 부분집합). + */ + select(props: IAgentSelectorProps): Promise; +} diff --git a/packages/agent/src/structures/IAgentSelectorProps.ts b/packages/agent/src/structures/IAgentSelectorProps.ts new file mode 100644 index 00000000000..aa32ba9a0e8 --- /dev/null +++ b/packages/agent/src/structures/IAgentSelectorProps.ts @@ -0,0 +1,21 @@ +import { IAgentMessage } from "./IAgentMessage"; +import { IAgentOperation } from "./IAgentOperation"; + +/** + * {@link IAgentSelector}에 주어지는 입력. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentSelectorProps { + /** 지금까지의 대화(중립 메시지). */ + messages: IAgentMessage[]; + + /** Agent에 등록된 모든 operation. */ + operations: IAgentOperation[]; + + /** {@link IAgentConfig.capacity}에서 온 capacity 힌트. */ + capacity?: number | undefined; + + /** 턴의 abort 핸들. */ + abortSignal?: AbortSignal | undefined; +} diff --git a/packages/agent/src/structures/IAgentSystemPrompt.ts b/packages/agent/src/structures/IAgentSystemPrompt.ts new file mode 100644 index 00000000000..153fd0f0547 --- /dev/null +++ b/packages/agent/src/structures/IAgentSystemPrompt.ts @@ -0,0 +1,15 @@ +/** + * Harness의 내장 system prompt를 단계별로 덮어쓰는 override. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentSystemPrompt { + /** 주 대화/결정 prompt. */ + main?: string | ((next: unknown) => string); + + /** 함수 선택 prompt(selector가 개입할 때만 사용). */ + select?: string | ((next: unknown) => string); + + /** 결과 설명("describe") prompt. */ + describe?: string | ((next: unknown) => string); +} diff --git a/packages/agent/src/structures/IAgentTextContent.ts b/packages/agent/src/structures/IAgentTextContent.ts new file mode 100644 index 00000000000..44ed750065f --- /dev/null +++ b/packages/agent/src/structures/IAgentTextContent.ts @@ -0,0 +1,12 @@ +/** + * 멀티모달 메시지의 일반 텍스트 content part. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentTextContent { + /** 판별자(discriminator). */ + type: "text"; + + /** 텍스트 본문. */ + text: string; +} diff --git a/packages/agent/src/structures/IAgentTextDeltaChunk.ts b/packages/agent/src/structures/IAgentTextDeltaChunk.ts new file mode 100644 index 00000000000..e7a9f201275 --- /dev/null +++ b/packages/agent/src/structures/IAgentTextDeltaChunk.ts @@ -0,0 +1,12 @@ +/** + * Adapter 스트림의 증분 assistant 텍스트 — 주 채널. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentTextDeltaChunk { + /** 판별자(discriminator). */ + type: "text-delta"; + + /** 이번에 추가된 텍스트 조각(누적이 아니라 delta). */ + delta: string; +} diff --git a/packages/agent/src/structures/IAgentTokenUsage.ts b/packages/agent/src/structures/IAgentTokenUsage.ts new file mode 100644 index 00000000000..29e442e97e3 --- /dev/null +++ b/packages/agent/src/structures/IAgentTokenUsage.ts @@ -0,0 +1,31 @@ +import { IAgentTokenUsageInput } from "./IAgentTokenUsageInput"; +import { IAgentTokenUsageOutput } from "./IAgentTokenUsageOutput"; + +/** + * 한 턴 또는 누적 대화의 토큰 회계. + * + * Agentica와 마찬가지로 수치는 추정이 아니라 provider가 **보고**한 값이다(streaming usage 이벤트 경유). + * {@link continuations}는 `@typia/agent` 고유 카운터로, 하나의 논리적 답변이 output-token 천장을 넘어 + * locked checkpoint로부터 몇 번 이어졌는지를 기록한다. 0이 아니면 그 답변이 단일 completion의 max output + * 토큰을 초과해 이어 붙여졌다는 뜻 — 이 라이브러리를 차별화하는 바로 그 feature다. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentTokenUsage { + /** 입력 + 출력 토큰의 총합. */ + total: number; + + /** 입력(프롬프트) 토큰 분해. */ + input: IAgentTokenUsageInput; + + /** 출력(completion) 토큰 분해. */ + output: IAgentTokenUsageOutput; + + /** + * 이 턴/누적에서 수행된 ceiling-continuation 횟수. + * + * `0` ⇒ 모든 답변이 단일 completion 안에 들어맞음. `> 0` ⇒ harness가 `length` finish를 감지하고 + * locked prefix로부터 이어 붙였음. + */ + continuations: number; +} diff --git a/packages/agent/src/structures/IAgentTokenUsageInput.ts b/packages/agent/src/structures/IAgentTokenUsageInput.ts new file mode 100644 index 00000000000..ca94d94a670 --- /dev/null +++ b/packages/agent/src/structures/IAgentTokenUsageInput.ts @@ -0,0 +1,12 @@ +/** + * {@link IAgentTokenUsage}의 입력(프롬프트) 측 토큰 분해. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentTokenUsageInput { + /** 입력 토큰 총합. */ + total: number; + + /** Provider의 prompt cache에서 제공된 토큰(보고될 경우). */ + cached: number; +} diff --git a/packages/agent/src/structures/IAgentTokenUsageOutput.ts b/packages/agent/src/structures/IAgentTokenUsageOutput.ts new file mode 100644 index 00000000000..3212b4f3ab9 --- /dev/null +++ b/packages/agent/src/structures/IAgentTokenUsageOutput.ts @@ -0,0 +1,12 @@ +/** + * {@link IAgentTokenUsage}의 출력(completion) 측 토큰 분해. + * + * @author Jeongho Nam - https://github.com/samchon + */ +export interface IAgentTokenUsageOutput { + /** 출력 토큰 총합. */ + total: number; + + /** Provider가 분리해 주는 경우의 reasoning/thinking 토큰. */ + reasoning: number; +} diff --git a/packages/agent/tsconfig.json b/packages/agent/tsconfig.json new file mode 100644 index 00000000000..21b4f744847 --- /dev/null +++ b/packages/agent/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../config/tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "lib", + }, + "include": ["src"], +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 79ec2af2f4b..8ee092d8115 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -256,6 +256,46 @@ importers: specifier: catalog:typescript version: 0.15.1 + packages/agent: + dependencies: + '@typia/interface': + specifier: workspace:^ + version: link:../interface + '@typia/utils': + specifier: workspace:^ + version: link:../utils + typia: + specifier: workspace:^ + version: link:../typia + devDependencies: + '@rollup/plugin-commonjs': + specifier: catalog:rollup + version: 29.0.2(rollup@4.60.2) + '@rollup/plugin-node-resolve': + specifier: catalog:rollup + version: 16.0.3(rollup@4.60.2) + '@typescript/native-preview': + specifier: catalog:typescript + version: 7.0.0-dev.20260527.2 + rimraf: + specifier: catalog:utils + version: 6.1.3 + rollup: + specifier: catalog:rollup + version: 4.60.2 + rollup-plugin-auto-external: + specifier: catalog:rollup + version: 2.0.0(rollup@4.60.2) + rollup-plugin-node-externals: + specifier: catalog:rollup + version: 8.1.2(rollup@4.60.2) + tinyglobby: + specifier: ^0.2.12 + version: 0.2.16 + ttsc: + specifier: catalog:typescript + version: 0.15.1 + packages/interface: devDependencies: '@typescript/native-preview':