본문으로 건너뛰기

Function 작성

Component presentation 경계

현재 @px.component는 companion metadata와 Python annotation 기반 port shape를 등록합니다. 현재 V1의 기존 presentation·settings wire는 호환을 위해 보존하고 production Flow reader는 Component target을 unsupported로 표시합니다. 후속 V2 Canvas는 package UI를 마운트하지 않고 host-owned component.declarative@2만 사용합니다. settings를 component-declared-settings@1로 선언당 최대 8개까지 받고 host control registry가 그리며, rich surface는 RFC-016이 소유하는 component-rich-surface-declaration@1 범위이고, bridge는 Node context 읽기와 settings 제안까지만 허용합니다.

Function은 한 번 구현해 여러 Node가 참조하는 실행 기능입니다. Function 하나에는 사용자가 이해할 수 있는 책임 하나를 두고, 내부 helper 함수는 @px.function(..., helpers=[...])로 선언해 같은 Function revision에 싣고, library 호출 pin은 dependencies={...}로 revision의 dependency lock 안에 유지합니다.

다른 사용자가 package를 설치하면 이 Function은 package의 public-module에서 일반 Python symbol로 import됩니다. @px.function은 reusable definition만 등록하며 Node를 배치하거나 계산을 실행하지 않습니다. 실제 사용처는 node = flow.node("node_key", shared_function)로 배치하고 flow.connect(...)로 연결합니다.

Node에서 실행할 Project-owned 계산도 @px.function으로 명시합니다. decorator 없는 def는 helper로 사용합니다. Function의 helper는 helpers=[...]로 선언해야 revision에 저장되고, Component의 helper는 executor module에 둡니다. Project Function source는 Definitions → Functions에서 편집하고, 변경 시 모든 placement의 영향을 함께 검토합니다. 설치 Component와 맞출 reusable companion target은 @px.component로 등록합니다. 후속 V2의 Canvas UI는 host-owned component.declarative@2 projection만 사용합니다. 자세한 배포·설치는 커스텀 Function 배포와 사용, Component 작성은 Component 작성과 사용을 참고하세요.

최소 Function

python
from pipelinexlab import px

class GirderResults(px.Results):
    utilization: float


@px.function(
    key="girder_resistance",
    version="1.0.0",
    description="주어진 경간의 거더 사용률을 계산합니다.",
)
def girder_resistance(span: float) -> GirderResults:
    utilization = calculate_utilization(span)
    return GirderResults(utilization=utilization)

versiondescription은 모든 public Function에 필수입니다. decorator argument에는 nodeKey, layout, connection과 Run trigger를 넣지 않습니다. version은 SemVer 문자열이며 FunctionDescriptor, 이를 참조하는 Node와 Run provenance에 사용됩니다. Flow-level port를 외부에 공개하면 저장된 Flow revision이 모든 Node target 조합을 고정합니다. 다른 Node가 result port를 직접 연결하면 exact package release, platform과 descriptor를 담은 FunctionRef가 expectedTargetReffunction variant로 기록됩니다. 실행 전 validator는 현재 Node target과 이 ref가 일치하는지 확인합니다.

선언의 단일 원본

정보유일한 선언 위치
Function key@px.function(key=...)의 명시적 stable key
Function version·description@px.function(...)
input key·typefunction parameter와 왼쪽 type annotation
result key·typepx.Results field와 왼쪽 type annotation
target port의 unit·descriptionpx.input(...) 또는 px.result(...) marker
target port의 constraint·connection계획 계약
requiredPython parameter default가 없는지 여부
defaultPython parameter의 일반 default 값
nullabletype에 None이 포함되는지 여부

Function의 파생 contract와 이 Function을 사용하는 Node를 함께 보여 주는 Contract & usage 화면

Contract & usage 화면 — source 선언에서 파생한 input/result 계약과 이 Function을 참조하는 Node를 함께 보여 줍니다.

현재 facade에서 required, nullable과 default는 일반 Python parameter annotation과 default에서 한 번 파생합니다. px.input(...) metadata marker는 그 자리에서 description과 unit을 더하며 이 페이지의 예제는 파생 규칙만 보여 줍니다. 현재 문법에서는 다음 의미가 파생됩니다.

python
def example(
    required_value: float,
    optional_value: float = 10.0,
    nullable_value: float | None = None,
) -> ExampleResults:
    ...
  • required_value: required, non-null, no default
  • optional_value: not required, non-null, default 10.0
  • nullable_value: not required, nullable, default None

nullable type인데 default가 없으면 값 자체는 required지만 명시적인 null은 허용합니다. SDK binding은 이 차이를 보존해야 합니다.

표준 Python type을 IDE와 정적 분석의 원본으로 사용하고, SDK는 marker metadata를 내부 TypeDescriptor로 변환합니다.

Type과 shape

Port에는 다음 중 정확한 type과 shape를 선언합니다.

  • scalar: number, integer, boolean, string, enum
  • record: 이름 있는 typed fields
  • list: 같은 element type의 순서 있는 collection
  • table: 이름 있는 column schema와 row records
  • object reference: PDF, image, Arrow table 또는 큰 binary

닫힌 record와 table schema를 사용하고 임의 key가 필요한 경우 그 이유와 bound를 명시합니다. Validator는 Result tuple, 위치 기반 result와 output0 같은 자동 이름을 거부합니다.

Unit

  • 물리량 숫자에는 canonical unit을 선언합니다.
  • 무차원 값은 unit "1"을 사용합니다.
  • 화면 표시 단위와 canonical 저장 단위를 구분할 수 있지만 자동 변환은 승인된 dimension 변환만 허용합니다.
  • 모든 물리량 port에 구조화 unit을 선언합니다.
  • validator는 같은 dimension의 호환되는 unit만 연결합니다.

Description

Function과 모든 public port는 사람이 읽고 AI가 같은 뜻으로 해석할 수 있는 description을 가집니다.

좋은 설명:

Bearing centre-line 사이에서 측정한 순경간입니다.

좋지 않은 설명:

Span 값입니다.

description에는 목적과 기준을 쓰고, type, unit, required와 constraint는 구조화 field에 둡니다. 이 canonical description을 PXFLOW와 AI가 사용하며, V2 host renderer도 같은 선언을 읽습니다. Python docstring은 개발자 도움말로 사용할 수 있습니다.

외부 연결 가능 여부

모든 input/result는 기본적으로 connection=True입니다. 내부 계산 trace처럼 외부 연결에 부적합한 port만 예외로 지정합니다.

PXFLOW, Report Workbench와 AI는 이 선언에서 같은 연결 후보를 읽습니다. Report는 Flow가 flow.result(...)로 공개한 port를 선택하고, 후속 V2의 host-owned component.declarative@2도 admitted declaration의 Port를 투영합니다. Extension별 template과 custom block은 해당 Extension manual을 따릅니다.

이 예제는 contract_only이며 현재 SDK에서 실행할 수 없습니다. 계획된 px.Evidencepx.result(..., connection=False) discovery metadata의 목표 형태만 설명합니다.

python
class SolverResults(px.Results):
    trace: px.Evidence = px.result(
        description="내부 solver 상태 확인용 trace입니다.",
        connection=False,
    )

connection=False의 정확한 효과:

  • host-owned Component discovery와 연결 후보에서 숨김
  • AI와 MCP의 일반 public interface discovery에서 숨김
  • 다른 Flow의 외부 연결 후보에서 숨김
  • 같은 Flow 내부의 명시적 flow.connect(...)에는 사용 가능

다음 항목은 별도 권한 계약에서 결정합니다.

  • Project read/write/run ACL
  • Function package 설치 권한
  • result resource 접근 권한
  • network, filesystem 또는 secret capability

@px.record와 Table·다중 결과

이 절은 planned rich type 계약입니다. 현재 collection port에는 지원되는 원소 annotation의 list[T]를 사용합니다. 향후 여러 field를 한 port 값으로 전달할 때 @px.record를 정의하며, 이는 data schema이고 Node나 실행 target이 아닙니다. 이 예제는 contract_only이며 현재 SDK에서 실행할 수 없습니다.

python
@px.record(
    "check_result",
    description="한 설계 case의 검토 결과입니다.",
    row_key="case_key",
)
class CheckResult:
    case_key: str = px.field(description="원본 case key입니다.")
    utilization: float = px.field(
        unit="1",
        description="수요-저항 비율입니다.",
    )


class GirderResults(px.Results):
    resistance_summary: px.Table[CheckResult] = px.result(
        description="각 설계 case의 저항 검토 결과입니다."
    )
    governing_utilization: float = px.result(
        unit="1",
        description="모든 case 중 가장 큰 사용률입니다.",
    )

CheckResult를 먼저 정의하므로 px.Table[CheckResult] annotation을 Python 3.12에서 바로 해석할 수 있습니다. 각 result field는 독립적인 portKey입니다. PXFLOW와 AI는 선언 순서 대신 resistance_summary 또는 governing_utilization을 선택합니다.

  • recordKey, field key와 row_key는 stable lower_snake_case입니다.
  • 각 field annotation이 type과 nullability를 소유하고 px.field(...)가 description, unit, constraint와 default를 추가합니다.
  • row_key로 지정한 field는 required·non-null str 또는 int입니다.
  • Table 결과를 Report에 표시하려면 Flow public result로 공개한 뒤 Report Workbench에서 result 위치에 mapping합니다. row_key는 갱신·정렬 뒤에도 같은 행을 식별합니다.
  • 큰 Table 결과는 Arrow/ObjectRef에 저장하고 bounded preview를 제공합니다.
  • row order가 의미 있으면 source order를 provenance에 보존합니다.

Function이 Table 전체를 한 번에 받는지 Record 한 개를 받는지는 input type으로 구분합니다. Table은 하나의 typed 값이며 scalar port에 연결할 수 없습니다. 같은 Flow를 여러 입력으로 실행하려면 사용자가 별도의 Batch Run을 만들고 각 실행의 input set을 명시합니다. Canvas 연결만으로 실행 수가 늘어나지 않습니다.

Side effect, 병렬 안전성과 capability

Function의 실행 성격과 권한 상한도 같은 decorator에서 선언합니다.

이 예제는 contract_only이며 현재 SDK에서 실행할 수 없습니다. px.policy(...), px.capabilities(...), planned Record input metadata를 결합한 목표 계약입니다.

python
@px.function(
    key="external_section_check",
    version="1.0.0",
    description="외부 설계 API를 사용해 단면을 검토합니다.",
    policy=px.policy(
        deterministic=False,
        parallel_safe=False,
        side_effect="external",
        cache="none",
        retry="never",
        timeout_seconds=60,
    ),
    capabilities=px.capabilities(
        files="none",
        network=("api.example.com",),
        secrets=("structural_api",),
        gpu=False,
    ),
)
def external_section_check(
    section: Section = px.input(description="검토할 단면입니다."),
) -> SectionCheckResults:
    ...
  • side_effect: none | local_write | external
  • cache: none | content
  • retry: never | safe
  • files: none | selected_read | project_read | project_write

Runtime은 parallel_safe=True, quota와 dependency를 함께 확인해 병렬 실행 여부를 결정합니다. Live는 실행 trigger로 사용합니다. local_writeexternal effect에는 cache="none", retry="never"를 지정합니다.

Capability는 필요한 file, network, secret과 GPU 범위만 선언합니다. 이 값은 권한 요청의 상한선이며, Runtime은 선언과 Project policy, 사용자 승인을 모두 검사합니다.

오류 작성

예상 가능한 업무 오류는 stable diagnostic으로 반환할 수 있어야 합니다.

  • stable code
  • 정확한 input/result path
  • locale-independent parameters
  • retryable 여부
  • 사용자가 수행할 수 있는 suggested action

사용자 메시지에는 stable code, 안전한 parameter와 suggested action만 포함합니다. Runtime은 secret, host path, 내부 식별자와 예상하지 못한 exception의 raw traceback을 격리하고 redact합니다.

Function 작성 체크리스트

  • [ ] @px.functionversiondescription이 있습니다.
  • [ ] Function key가 stable lower_snake_case입니다.
  • [ ] 현재 parameter 이름과 px.Results annotated field 이름만 port key를 소유합니다.
  • [ ] required/null/default가 일반 Python annotation과 default에서 한 번만 파생됩니다.
  • [ ] 모든 public port가 지원되는 정확한 type과 description을 가집니다.
  • [ ] 공학 숫자가 canonical unit을 가집니다.
  • [ ] 현재 collection은 list[T]이며 planned Record/Table 계약은 contract_only로 구분했습니다.
  • [ ] planned Table row 계약에는 stable row_key가 있습니다.
  • [ ] 다중 결과가 이름 있는 px.Results field입니다.
  • [ ] 연결을 숨겨야 하는 port에만 connection=False를 썼습니다.
  • [ ] connection, ACL과 capability를 각각 독립된 계약으로 선언했습니다.
  • [ ] side effect, resource와 병렬 안전성이 machine-readable합니다.
  • [ ] 정상·경계·invalid·취소 test가 있습니다.

기준 문법