@things-factory/figure-service 10.1.26 → 10.1.27
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/figure/figure-propose.js +20 -3
- package/dist-server/service/figure/figure-propose.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/server/service/figure/figure-propose.test.ts +11 -1
- package/server/service/figure/figure-propose.ts +22 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/figure-service",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.27",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public",
|
|
6
6
|
"@things-factory:registry": "https://registry.npmjs.org"
|
|
@@ -26,12 +26,12 @@
|
|
|
26
26
|
"@hatiolab/figure-model": "^0.1.21",
|
|
27
27
|
"@operato/typeorm-history": "^10.0.0",
|
|
28
28
|
"@things-factory/ai-client-base": "^10.1.26",
|
|
29
|
-
"@things-factory/auth-base": "^10.1.
|
|
29
|
+
"@things-factory/auth-base": "^10.1.27",
|
|
30
30
|
"@things-factory/env": "^10.1.20",
|
|
31
|
-
"@things-factory/shell": "^10.1.
|
|
31
|
+
"@things-factory/shell": "^10.1.27",
|
|
32
32
|
"graphql-scalars": "^1.22.4",
|
|
33
33
|
"graphql-upload": "^15.0.2",
|
|
34
34
|
"koa-router": "^7.4.0"
|
|
35
35
|
},
|
|
36
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "d04dc762cacf2aac8ba0b0f5d768eca365fc09da"
|
|
37
37
|
}
|
|
@@ -255,6 +255,16 @@ describe('프롬프트는 형식을 따라간다 — 손으로 적은 목록이
|
|
|
255
255
|
}
|
|
256
256
|
})
|
|
257
257
|
|
|
258
|
+
describe('joints (ADR-0066)', () => {
|
|
259
|
+
it('teaches the joint shape with the type vocabulary, and a joint channel with no path and a number per key', () => {
|
|
260
|
+
const prompt = rulesPrompt(PALETTE)
|
|
261
|
+
expect(prompt).toContain(`type: ${(model as any).JOINT_TYPES.join(' | ')}`)
|
|
262
|
+
expect(prompt).toMatch(/NO path, NO pivot/)
|
|
263
|
+
expect(prompt).toMatch(/limits must include 0/)
|
|
264
|
+
expect(prompt).toMatch(/ONLY place a parent is written/)
|
|
265
|
+
})
|
|
266
|
+
})
|
|
267
|
+
|
|
258
268
|
describe('고치라는 요청일 때', () => {
|
|
259
269
|
it('요청이 안 건드린 것을 그대로 실으라고 못 박는다', async () => {
|
|
260
270
|
/*
|
|
@@ -270,7 +280,7 @@ describe('고치라는 요청일 때', () => {
|
|
|
270
280
|
expect(sent).toMatch(/dropping one is silent/)
|
|
271
281
|
|
|
272
282
|
/* 잃기 쉬운 칸은 이름으로 짚어 준다 — 「전부 지켜라」만으로는 모델이 빠뜨린다. */
|
|
273
|
-
for (const field of ['animations', 'capabilities', 'anchor', 'repeat', 'label', 'materialSlot']) {
|
|
283
|
+
for (const field of ['animations', 'parameters', 'joints', 'capabilities', 'parent', 'anchor', 'repeat', 'label', 'materialSlot']) {
|
|
274
284
|
expect(sent).toContain(field)
|
|
275
285
|
}
|
|
276
286
|
})
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
SEGMENT_PRESETS,
|
|
18
18
|
SIZING_RULES,
|
|
19
19
|
FIGURE_PLACEMENTS,
|
|
20
|
+
JOINT_TYPES,
|
|
20
21
|
FIGURE_SOURCE_VERSION,
|
|
21
22
|
compile,
|
|
22
23
|
costOf,
|
|
@@ -148,12 +149,12 @@ export function rulesPrompt(palette: string[]): string {
|
|
|
148
149
|
'',
|
|
149
150
|
'The JSON is a FigureSource:',
|
|
150
151
|
` { version: ${FIGURE_SOURCE_VERSION}, type, base: {x,y,z}, placement?, detailLevel, parts: [...],`,
|
|
151
|
-
' animations?: [...], parameters?: [...], capabilities?: [...] }',
|
|
152
|
+
' animations?: [...], parameters?: [...], capabilities?: [...], joints?: [...] }',
|
|
152
153
|
'',
|
|
153
154
|
'A part is:',
|
|
154
155
|
' { name, primitive, transform: { position: {x,y,z}, size: {x,y,z}, rotation?: {x,y,z} },',
|
|
155
156
|
' material: { token, preset?, flatShading?, transparent?, emissive? },',
|
|
156
|
-
' segments?, shape?, materialSlot?, keepRound?, sizing?, anchor?, repeat?, label?, capability? }',
|
|
157
|
+
' segments?, shape?, materialSlot?, keepRound?, sizing?, anchor?, repeat?, label?, capability?, parent? }',
|
|
157
158
|
'',
|
|
158
159
|
'Hard vocabulary — anything outside these is rejected:',
|
|
159
160
|
` placement ${FIGURE_PLACEMENTS.join(' | ')} (floor: default, ground equipment; ceiling: overhead hoist/OHT; center: airborne/drone/sensor)`,
|
|
@@ -274,6 +275,23 @@ export function rulesPrompt(palette: string[]): string {
|
|
|
274
275
|
' Do NOT answer it with a capability. Capabilities say what a figure does in the FLOW;',
|
|
275
276
|
' how a pose is driven is a different axis entirely.',
|
|
276
277
|
'',
|
|
278
|
+
'Joints — a part that moves RELATIVE TO ANOTHER PART, carrying what is attached to it:',
|
|
279
|
+
' An arm whose shoulder turns and carries the elbow, wrist and gripper; a crane whose bridge',
|
|
280
|
+
' slides and carries the trolley and hook. Without joints every channel moves one part alone,',
|
|
281
|
+
' so two turning links do not compose.',
|
|
282
|
+
' parent on a part names the part it is attached to. It is the ONLY place a parent is written.',
|
|
283
|
+
' A part without parent hangs from the figure. Following parents must end; no loops.',
|
|
284
|
+
` joints: [{ name, child, type: ${JOINT_TYPES.join(' | ')}, origin: {x,y,z}, axis: {x,y,z}, limits?: { min, max } }]`,
|
|
285
|
+
' child is the part that moves; its parent is what it moves relative to. One joint per part.',
|
|
286
|
+
' origin is the point it turns about or slides from, in the same coordinates as part',
|
|
287
|
+
' positions (y from the bottom of the base). axis is the direction: turning follows the',
|
|
288
|
+
' right-hand rule. revolute and continuous are in degrees, prismatic in mm.',
|
|
289
|
+
' Parts are drawn in the pose where every joint is 0, so limits must include 0.',
|
|
290
|
+
' continuous has no limits. Joint names and part names must all be different.',
|
|
291
|
+
' A joint moves only through a parameter (or an animation for endless turning): a channel',
|
|
292
|
+
' { target: <joint name>, keys: [{ at, value: <number> }] } — NO path, NO pivot, and each',
|
|
293
|
+
' value is the joint coordinate, within limits. A 6-axis arm is six joints and six parameters.',
|
|
294
|
+
'',
|
|
277
295
|
' Names are shared between the two lists: an instance addresses a curve by name, so an',
|
|
278
296
|
' animation and a parameter may not be called the same thing.',
|
|
279
297
|
' Doors that open together belong in one curve with two channels, not two curves.',
|
|
@@ -295,8 +313,8 @@ function taskPrompt(input: ProposeInput): string {
|
|
|
295
313
|
'',
|
|
296
314
|
'Revise this existing figure. Keep the part names that still mean the same thing —',
|
|
297
315
|
'renaming a part breaks bindings on instances already placed.',
|
|
298
|
-
'Carry over everything the request did not ask you to change. animations,
|
|
299
|
-
'capability, anchor, repeat, label and materialSlot are easy to drop by writing them out of',
|
|
316
|
+
'Carry over everything the request did not ask you to change. animations, parameters, joints,',
|
|
317
|
+
'capabilities, capability, parent, anchor, repeat, label and materialSlot are easy to drop by writing them out of',
|
|
300
318
|
'the JSON, and dropping one is silent — the figure still validates, it just stops doing what',
|
|
301
319
|
'it did. If the request says nothing about movement, the clips come back unchanged.',
|
|
302
320
|
JSON.stringify(input.base)
|