@things-factory/board-import 10.1.154 → 10.1.155
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-server/service/adapters/image-adapter.d.ts +15 -0
- package/dist-server/service/adapters/image-adapter.js +2 -2
- package/dist-server/service/adapters/image-adapter.js.map +1 -1
- package/dist-server/service/ai/types.d.ts +14 -8
- package/dist-server/service/ai/types.js +11 -20
- package/dist-server/service/ai/types.js.map +1 -1
- package/dist-server/service/extraction/contracts.d.ts +2 -0
- package/dist-server/service/extraction/contracts.js.map +1 -1
- package/dist-server/service/extraction/vlm-recognizer.d.ts +1 -1
- package/dist-server/service/extraction/vlm-recognizer.js +2 -12
- package/dist-server/service/extraction/vlm-recognizer.js.map +1 -1
- package/dist-server/service/import-session/import-session.d.ts +1 -0
- package/dist-server/service/import-session/import-session.js +4 -0
- package/dist-server/service/import-session/import-session.js.map +1 -1
- package/dist-server/service/import-session/import-worker.js +4 -1
- package/dist-server/service/import-session/import-worker.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/server/service/adapters/image-adapter.ts +8 -3
- package/server/service/ai/types.ts +12 -19
- package/server/service/extraction/contracts.ts +2 -0
- package/server/service/extraction/extraction.test.ts +8 -2
- package/server/service/extraction/vlm-recognizer.ts +5 -4
- package/server/service/import-session/import-session.ts +3 -0
- package/server/service/import-session/import-worker.ts +4 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/board-import",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.155",
|
|
4
4
|
"main": "dist-server/index.js",
|
|
5
5
|
"things-factory": true,
|
|
6
6
|
"author": "heartyoh",
|
|
@@ -23,13 +23,13 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@operato/graphql": "^10.0.0",
|
|
26
|
-
"@things-factory/ai-client-base": "^10.1.
|
|
27
|
-
"@things-factory/attachment-base": "^10.1.
|
|
28
|
-
"@things-factory/auth-base": "^10.1.
|
|
29
|
-
"@things-factory/board-service": "^10.1.
|
|
26
|
+
"@things-factory/ai-client-base": "^10.1.155",
|
|
27
|
+
"@things-factory/attachment-base": "^10.1.155",
|
|
28
|
+
"@things-factory/auth-base": "^10.1.155",
|
|
29
|
+
"@things-factory/board-service": "^10.1.155",
|
|
30
30
|
"@things-factory/env": "^10.1.149",
|
|
31
|
-
"@things-factory/shell": "^10.1.
|
|
31
|
+
"@things-factory/shell": "^10.1.155",
|
|
32
32
|
"dxf-parser": "^1.1.2"
|
|
33
33
|
},
|
|
34
|
-
"gitHead": "
|
|
34
|
+
"gitHead": "0211c9c0764a26c10151fdc7d60094fc334905ff"
|
|
35
35
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* zero-to-twin 메인 트랙 (조감도/사진 업로드 → 보드) 의 첫 어댑터.
|
|
9
9
|
*/
|
|
10
|
-
import { getAIClient } from '../ai/types'
|
|
10
|
+
import { getAIClient, type BoardImportAI } from '../ai/types'
|
|
11
11
|
import type {
|
|
12
12
|
AdapterCapabilities,
|
|
13
13
|
AdapterInput,
|
|
@@ -21,6 +21,11 @@ import type {
|
|
|
21
21
|
const IMAGE_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.webp', '.gif', '.bmp']
|
|
22
22
|
|
|
23
23
|
export interface ImageParseOptions extends ParseOptions {
|
|
24
|
+
/**
|
|
25
|
+
* The AI of the domain the import runs for - asked only when an image is parsed, so a drawing that needs no AI
|
|
26
|
+
* imports in a domain with no AI agent.
|
|
27
|
+
*/
|
|
28
|
+
resolveAI?: () => Promise<BoardImportAI>
|
|
24
29
|
/** 자연어 컨텍스트 — VLM 에 함께 전달. 예: "factory floor plan with AGV paths and stockers" */
|
|
25
30
|
context?: string
|
|
26
31
|
/** 후보 카테고리 — VLM 이 그 안에서 라벨링하도록 가이드 */
|
|
@@ -62,9 +67,9 @@ export class ImageAdapter implements FormatAdapter {
|
|
|
62
67
|
}
|
|
63
68
|
|
|
64
69
|
async parse(file: AdapterInput, options: ImageParseOptions = {}): Promise<ParseResult> {
|
|
65
|
-
const ai = getAIClient()
|
|
70
|
+
const ai = getAIClient() ?? (await options.resolveAI?.())
|
|
66
71
|
if (!ai) {
|
|
67
|
-
throw new Error('Image import
|
|
72
|
+
throw new Error('Image import needs the AI of the domain it runs for (resolveAI) or a registered BoardImportAI.')
|
|
68
73
|
}
|
|
69
74
|
if (!ai.parseFromImage) {
|
|
70
75
|
throw new Error('Registered BoardImportAI does not implement parseFromImage (no VLM support).')
|
|
@@ -7,17 +7,17 @@
|
|
|
7
7
|
*
|
|
8
8
|
* 사용 예:
|
|
9
9
|
* ```ts
|
|
10
|
-
* import {
|
|
10
|
+
* import { aiClientFor } from '@things-factory/ai-client-base'
|
|
11
11
|
* import { setAIClient, DefaultBoardImportAI } from '@things-factory/board-import'
|
|
12
12
|
*
|
|
13
|
-
* const base =
|
|
13
|
+
* const base = await aiClientFor({ domain, user }) // the domain's AI agent
|
|
14
14
|
* setAIClient(new DefaultBoardImportAI(base))
|
|
15
15
|
* ```
|
|
16
16
|
*
|
|
17
17
|
* Phase 1-2 결정적 파이프라인은 AI 미사용. 위 등록은 Phase 3+ 메서드 활성화용.
|
|
18
18
|
*/
|
|
19
19
|
import {
|
|
20
|
-
|
|
20
|
+
aiClientFor,
|
|
21
21
|
parseJSONResponse,
|
|
22
22
|
type AIClient as BaseAIClient
|
|
23
23
|
} from '@things-factory/ai-client-base'
|
|
@@ -590,29 +590,22 @@ function hydrateRuleRegexes<T extends { match?: any }>(rule: T): T {
|
|
|
590
590
|
// ── Provider 등록 ──────────────────────────────────────────────────
|
|
591
591
|
|
|
592
592
|
let currentClient: BoardImportAI | undefined
|
|
593
|
-
let defaultBackedClient: BoardImportAI | undefined
|
|
594
|
-
let defaultBackedFor: BaseAIClient | undefined
|
|
595
593
|
|
|
594
|
+
/** A BoardImportAI that answers every import, whatever the domain - for tests and for a host that brings its own. */
|
|
596
595
|
export function setAIClient(client: BoardImportAI | undefined): void {
|
|
597
596
|
currentClient = client
|
|
598
597
|
}
|
|
599
598
|
|
|
599
|
+
export function getAIClient(): BoardImportAI | undefined {
|
|
600
|
+
return currentClient
|
|
601
|
+
}
|
|
602
|
+
|
|
600
603
|
/**
|
|
601
|
-
*
|
|
602
|
-
*
|
|
603
|
-
* 감싸서 반환 — board-ai 와 동일한 1회 설정만으로 image import 도 동작하게 한다.
|
|
604
|
-
*
|
|
605
|
-
* default base 가 바뀌면 (테스트 등) 캐시를 무효화하고 다시 감싼다.
|
|
604
|
+
* The BoardImportAI of a domain's AI agent (design/ai/agent-settings.md). An import runs in the background, after the
|
|
605
|
+
* request is gone, so the job asks with the domain and the person it stored.
|
|
606
606
|
*/
|
|
607
|
-
export function
|
|
608
|
-
|
|
609
|
-
const base = getDefaultAIClient()
|
|
610
|
-
if (!base) return undefined
|
|
611
|
-
if (defaultBackedFor !== base) {
|
|
612
|
-
defaultBackedFor = base
|
|
613
|
-
defaultBackedClient = new DefaultBoardImportAI(base)
|
|
614
|
-
}
|
|
615
|
-
return defaultBackedClient
|
|
607
|
+
export async function boardImportAIFor(request: { domain: { id: string }; user?: { id: string } | null }): Promise<BoardImportAI> {
|
|
608
|
+
return currentClient ?? new DefaultBoardImportAI(await aiClientFor(request as any))
|
|
616
609
|
}
|
|
617
610
|
|
|
618
611
|
/** Deprecated alias — 기존 코드 호환용. 새로 작성 시 BoardImportAI 사용. */
|
|
@@ -71,6 +71,8 @@ export interface ExtractionContext {
|
|
|
71
71
|
* 스케일바). ScaleCalibrator 가 최우선 사용. unitsPerPixel = units / pixels.
|
|
72
72
|
*/
|
|
73
73
|
scaleReference?: { pixels: number; units: number; unit?: string }
|
|
74
|
+
/** The AI client of the domain the extraction runs for (aiClientFor). A recognizer that asks a model needs it. */
|
|
75
|
+
aiClient?: import('@things-factory/ai-client-base').AIClient
|
|
74
76
|
[k: string]: any
|
|
75
77
|
}
|
|
76
78
|
|
|
@@ -253,8 +253,14 @@ describe('VlmRecognizer', () => {
|
|
|
253
253
|
expect(out.confidence).toBeLessThanOrEqual(0.3)
|
|
254
254
|
})
|
|
255
255
|
|
|
256
|
-
it('
|
|
257
|
-
await expect(new VlmRecognizer().recognize(prim())).rejects.toThrow(/
|
|
256
|
+
it('AI client 없으면 명확히 throw — 실행하는 쪽이 그 사업장 에이전트의 client 를 넘긴다', async () => {
|
|
257
|
+
await expect(new VlmRecognizer().recognize(prim())).rejects.toThrow(/needs the AI client of the domain/)
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
it('실행하는 쪽이 넘긴 ctx.aiClient 로 판정한다', async () => {
|
|
261
|
+
const client: any = { generateJSON: async () => ({ componentType: 'rack', confidence: 0.9 }) }
|
|
262
|
+
const r = await new VlmRecognizer().recognize(prim(), { aiClient: client })
|
|
263
|
+
expect(r.componentType).toBe('rack')
|
|
258
264
|
})
|
|
259
265
|
|
|
260
266
|
it('프롬프트에 region 신호(라벨·크기) + 카탈로그 포함', async () => {
|
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
* *의미 인식* 만: 주어진 신호(라벨·모양·크기·레이어) + 카탈로그 → componentType.
|
|
6
6
|
* 카탈로그(ctx.catalog) 가 있으면 그 안의 값만 허용(그라운딩).
|
|
7
7
|
*
|
|
8
|
-
* provider 는 ai-client-base 의 AIClient.
|
|
8
|
+
* provider 는 ai-client-base 의 AIClient. 생성자로 주입하거나(테스트), 실행하는 쪽이 그 사업장 에이전트의 client 를
|
|
9
|
+
* ctx.aiClient 로 넘긴다.
|
|
9
10
|
*/
|
|
10
|
-
import {
|
|
11
|
+
import type { AIClient } from '@things-factory/ai-client-base'
|
|
11
12
|
import type { Recognizer, RecognitionResult, GeometricPrimitive, ExtractionContext } from './contracts'
|
|
12
13
|
|
|
13
14
|
interface RawRecognition {
|
|
@@ -27,9 +28,9 @@ export class VlmRecognizer implements Recognizer {
|
|
|
27
28
|
constructor(private readonly client?: AIClient) {}
|
|
28
29
|
|
|
29
30
|
async recognize(region: GeometricPrimitive, ctx?: ExtractionContext): Promise<RecognitionResult> {
|
|
30
|
-
const client = this.client ??
|
|
31
|
+
const client = this.client ?? ctx?.aiClient
|
|
31
32
|
if (!client) {
|
|
32
|
-
throw new Error('[board-import/extraction] VlmRecognizer
|
|
33
|
+
throw new Error('[board-import/extraction] VlmRecognizer needs the AI client of the domain it runs for (ctx.aiClient).')
|
|
33
34
|
}
|
|
34
35
|
const raw = await client.generateJSON<RawRecognition>(buildPrompt(region, ctx), {
|
|
35
36
|
systemPrompt: SYSTEM,
|
|
@@ -127,6 +127,9 @@ export class ImportSession {
|
|
|
127
127
|
@Field(type => User, { nullable: true })
|
|
128
128
|
creator?: User
|
|
129
129
|
|
|
130
|
+
@RelationId((session: ImportSession) => session.creator)
|
|
131
|
+
creatorId?: string
|
|
132
|
+
|
|
130
133
|
@CreateDateColumn()
|
|
131
134
|
@Field({ nullable: true })
|
|
132
135
|
createdAt?: Date
|
|
@@ -16,6 +16,7 @@ import { Attachment } from '@things-factory/attachment-base'
|
|
|
16
16
|
import { STORAGE } from '@things-factory/attachment-base'
|
|
17
17
|
|
|
18
18
|
import { importBoardVariants } from '../pipeline/index'
|
|
19
|
+
import { boardImportAIFor } from '../ai/types'
|
|
19
20
|
import { ImportSession } from './import-session'
|
|
20
21
|
|
|
21
22
|
export async function runImportJob(sessionId: string): Promise<void> {
|
|
@@ -50,7 +51,9 @@ export async function runImportJob(sessionId: string): Promise<void> {
|
|
|
50
51
|
// 다른 adapter (DXF) 에는 무해 (몰라도 무시).
|
|
51
52
|
const mergedParseOptions = {
|
|
52
53
|
...(opts.parseOptions || {}),
|
|
53
|
-
...(opts.userPrompt ? { userPrompt: opts.userPrompt } : {})
|
|
54
|
+
...(opts.userPrompt ? { userPrompt: opts.userPrompt } : {}),
|
|
55
|
+
/* The domain's AI agent, asked only if the drawing needs it (design/ai/agent-settings.md). */
|
|
56
|
+
resolveAI: () => boardImportAIFor({ domain: { id: session.domainId! }, user: session.creatorId ? { id: session.creatorId } : null })
|
|
54
57
|
}
|
|
55
58
|
// AI 가 같은 도면을 다른 관점으로 해석한 multi-variant. parse 한 번, 시안별 mapping + assemble.
|
|
56
59
|
// DXF 등 single-variant adapter 는 1 boardModel 만.
|