codeflowhub 0.2.3__tar.gz → 0.3.0__tar.gz
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.
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/PKG-INFO +1 -1
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/flow.py +2 -2
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/service/airflow_exporter.py +103 -1
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/task.py +4 -1
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub.egg-info/PKG-INFO +1 -1
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/setup.py +1 -1
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/LICENSE +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/README.md +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/__init__.py +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/action.py +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/airflow/__init__.py +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/airflow/xcom.py +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/base.py +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/model.py +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/service/__init__.py +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/storage/__init__.py +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/storage/local_storage.py +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/storage/s3_storage.py +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/storage/storage.py +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/template/__init__.py +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/template/analyze_speaker_pkg/__init__.py +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/template/analyze_speaker_pkg/main.py +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/template/extract_voice_pkg/__init__.py +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/template/extract_voice_pkg/main.py +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/template/read_pdf_pkg/__init__.py +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/template/read_pdf_pkg/main.py +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/template/transcript_pkg/__init__.py +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/template/transcript_pkg/main.py +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub.egg-info/SOURCES.txt +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub.egg-info/dependency_links.txt +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub.egg-info/top_level.txt +0 -0
- {codeflowhub-0.2.3 → codeflowhub-0.3.0}/setup.cfg +0 -0
|
@@ -353,8 +353,8 @@ class FlowDecorator(BaseDecorator):
|
|
|
353
353
|
"""개별 task 결과를 run.json에 저장"""
|
|
354
354
|
run_log = self._load_log_file()
|
|
355
355
|
run_log[task_name] = {
|
|
356
|
-
'input': result,
|
|
357
|
-
'output': result
|
|
356
|
+
'input': self._strip_context(result),
|
|
357
|
+
'output': self._strip_context(result)
|
|
358
358
|
}
|
|
359
359
|
self._write_log_file(run_log)
|
|
360
360
|
|
|
@@ -283,6 +283,7 @@ base_volume_mounts = [
|
|
|
283
283
|
|
|
284
284
|
tolerations_code = self._build_tolerations_code(task)
|
|
285
285
|
node_selector_code = self._build_node_selector_code(task)
|
|
286
|
+
affinity_code = self._build_affinity_code(task)
|
|
286
287
|
volume_mounts_code = self._build_volume_mounts_code(task)
|
|
287
288
|
container_resources_code = self._build_container_resources_code(task)
|
|
288
289
|
sidecars_code = self._build_sidecars_code(task)
|
|
@@ -304,7 +305,7 @@ base_volume_mounts = [
|
|
|
304
305
|
operator_code = f''' {task.name} = KubernetesPodOperator(
|
|
305
306
|
**common,
|
|
306
307
|
task_id='{task.name}',
|
|
307
|
-
image='{task_image}',{pool_code}{trigger_rule_code}{retries_code}{env_vars_code}{tolerations_code}{node_selector_code}{volume_mounts_code}{container_resources_code}{sidecars_code}
|
|
308
|
+
image='{task_image}',{pool_code}{trigger_rule_code}{retries_code}{env_vars_code}{tolerations_code}{node_selector_code}{affinity_code}{volume_mounts_code}{container_resources_code}{sidecars_code}
|
|
308
309
|
arguments=[
|
|
309
310
|
f\'\'\'{arguments}\'\'\'
|
|
310
311
|
],
|
|
@@ -361,6 +362,107 @@ base_volume_mounts = [
|
|
|
361
362
|
return f"\n node_selector={repr(task.node_selector)},"
|
|
362
363
|
return ""
|
|
363
364
|
|
|
365
|
+
def _build_affinity_code(self, task):
|
|
366
|
+
"""Affinity 코드 생성 (node_affinity 지원)
|
|
367
|
+
|
|
368
|
+
affinity dict는 K8s YAML 구조를 그대로 따르며, camelCase/snake_case 모두 허용.
|
|
369
|
+
예:
|
|
370
|
+
affinity={
|
|
371
|
+
'node_affinity': {
|
|
372
|
+
'required_during_scheduling_ignored_during_execution': {
|
|
373
|
+
'node_selector_terms': [
|
|
374
|
+
{'match_expressions': [
|
|
375
|
+
{'key': 'workload-type/spot', 'operator': 'In', 'values': ['true']}
|
|
376
|
+
]}
|
|
377
|
+
]
|
|
378
|
+
},
|
|
379
|
+
'preferred_during_scheduling_ignored_during_execution': [
|
|
380
|
+
{'weight': 100, 'preference': {
|
|
381
|
+
'match_expressions': [
|
|
382
|
+
{'key': 'workload-type/spot', 'operator': 'In', 'values': ['true']}
|
|
383
|
+
]
|
|
384
|
+
}}
|
|
385
|
+
]
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
"""
|
|
389
|
+
if not (hasattr(task, 'affinity') and task.affinity):
|
|
390
|
+
return ""
|
|
391
|
+
|
|
392
|
+
aff = self._normalize_keys(task.affinity)
|
|
393
|
+
parts = []
|
|
394
|
+
if 'node_affinity' in aff:
|
|
395
|
+
node_aff_code = self._render_node_affinity(aff['node_affinity'])
|
|
396
|
+
if node_aff_code:
|
|
397
|
+
parts.append(f"node_affinity={node_aff_code}")
|
|
398
|
+
|
|
399
|
+
if not parts:
|
|
400
|
+
return ""
|
|
401
|
+
|
|
402
|
+
return f"\n affinity=k8s.V1Affinity({', '.join(parts)}),"
|
|
403
|
+
|
|
404
|
+
def _render_node_affinity(self, node_aff):
|
|
405
|
+
"""k8s.V1NodeAffinity(...) 코드 생성"""
|
|
406
|
+
na = self._normalize_keys(node_aff)
|
|
407
|
+
parts = []
|
|
408
|
+
|
|
409
|
+
if 'required_during_scheduling_ignored_during_execution' in na:
|
|
410
|
+
required = self._normalize_keys(na['required_during_scheduling_ignored_during_execution'])
|
|
411
|
+
terms = required.get('node_selector_terms', [])
|
|
412
|
+
terms_code = ', '.join(self._render_node_selector_term(t) for t in terms)
|
|
413
|
+
parts.append(
|
|
414
|
+
f"required_during_scheduling_ignored_during_execution="
|
|
415
|
+
f"k8s.V1NodeSelector(node_selector_terms=[{terms_code}])"
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
if 'preferred_during_scheduling_ignored_during_execution' in na:
|
|
419
|
+
prefs = na['preferred_during_scheduling_ignored_during_execution'] or []
|
|
420
|
+
pref_codes = []
|
|
421
|
+
for pref in prefs:
|
|
422
|
+
p = self._normalize_keys(pref)
|
|
423
|
+
weight = p.get('weight', 1)
|
|
424
|
+
preference = self._render_node_selector_term(p.get('preference', {}))
|
|
425
|
+
pref_codes.append(
|
|
426
|
+
f"k8s.V1PreferredSchedulingTerm(weight={weight}, preference={preference})"
|
|
427
|
+
)
|
|
428
|
+
parts.append(
|
|
429
|
+
f"preferred_during_scheduling_ignored_during_execution=[{', '.join(pref_codes)}]"
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
return f"k8s.V1NodeAffinity({', '.join(parts)})"
|
|
433
|
+
|
|
434
|
+
def _render_node_selector_term(self, term):
|
|
435
|
+
"""k8s.V1NodeSelectorTerm(...) 코드 생성"""
|
|
436
|
+
t = self._normalize_keys(term)
|
|
437
|
+
parts = []
|
|
438
|
+
|
|
439
|
+
for field_key, api_key in [('match_expressions', 'match_expressions'),
|
|
440
|
+
('match_fields', 'match_fields')]:
|
|
441
|
+
if field_key in t:
|
|
442
|
+
req_codes = [self._render_node_selector_requirement(r) for r in t[field_key]]
|
|
443
|
+
parts.append(f"{api_key}=[{', '.join(req_codes)}]")
|
|
444
|
+
|
|
445
|
+
return f"k8s.V1NodeSelectorTerm({', '.join(parts)})"
|
|
446
|
+
|
|
447
|
+
def _render_node_selector_requirement(self, req):
|
|
448
|
+
"""k8s.V1NodeSelectorRequirement(...) 코드 생성"""
|
|
449
|
+
r = self._normalize_keys(req)
|
|
450
|
+
req_parts = [f"key={repr(r['key'])}", f"operator={repr(r['operator'])}"]
|
|
451
|
+
if 'values' in r:
|
|
452
|
+
req_parts.append(f"values={repr(list(r['values']))}")
|
|
453
|
+
return f"k8s.V1NodeSelectorRequirement({', '.join(req_parts)})"
|
|
454
|
+
|
|
455
|
+
@staticmethod
|
|
456
|
+
def _normalize_keys(d):
|
|
457
|
+
"""dict의 최상위 키를 camelCase → snake_case로 정규화"""
|
|
458
|
+
import re
|
|
459
|
+
if not isinstance(d, dict):
|
|
460
|
+
return d
|
|
461
|
+
return {
|
|
462
|
+
re.sub(r'([A-Z])', r'_\1', k).lower().lstrip('_'): v
|
|
463
|
+
for k, v in d.items()
|
|
464
|
+
}
|
|
465
|
+
|
|
364
466
|
def _build_volume_mounts_code(self, task, include_base=True):
|
|
365
467
|
"""Volume mounts 코드 생성
|
|
366
468
|
|
|
@@ -13,11 +13,13 @@ class TaskDecorator(BaseDecorator):
|
|
|
13
13
|
tolerations: list[Toleration]
|
|
14
14
|
volume_mounts: list[VolumeMount]
|
|
15
15
|
sidecars: list[SidecarContainer]
|
|
16
|
+
affinity: dict
|
|
16
17
|
|
|
17
18
|
def __init__(self, *args, cpu='1', memory='1Gi', gpu=None, image=None,
|
|
18
19
|
node_selector=None, tolerations: list[Toleration] = None,
|
|
19
20
|
volume_mounts: list[VolumeMount] = None, pool: str = None,
|
|
20
|
-
sidecars: list[SidecarContainer] = None,
|
|
21
|
+
sidecars: list[SidecarContainer] = None, affinity: dict = None,
|
|
22
|
+
**kwargs):
|
|
21
23
|
super().__init__(*args, **kwargs)
|
|
22
24
|
self.set_resource(cpu, memory, gpu)
|
|
23
25
|
self.image = image
|
|
@@ -26,6 +28,7 @@ class TaskDecorator(BaseDecorator):
|
|
|
26
28
|
self.volume_mounts = volume_mounts or []
|
|
27
29
|
self.pool = pool
|
|
28
30
|
self.sidecars = sidecars or []
|
|
31
|
+
self.affinity = affinity or {}
|
|
29
32
|
self._is_flowhub_task = True
|
|
30
33
|
|
|
31
34
|
def set_resource(self, cpu, memory, gpu):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codeflowhub-0.2.3 → codeflowhub-0.3.0}/codeflowhub/template/analyze_speaker_pkg/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|