Accessing node and graph metadata/context from within a node
Maintainer thường phản hồi trong vòng 2 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức phù hợp với người mới
- 25/100
- Loại issue
- Tính năng
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Đình trệ
- Công nghệ
- python
- Lĩnh vực
- data-engineering, observability
Hướng nghiên cứu
Bắt đầu bằng việc xem xét các hook vòng đời pre_node_execute và post_node_execute cùng đường dẫn thực thi async; issue cũng liên kết đến dmexs/hamilton-contextvar-repro. So sánh cách tiếp cận contextvars với các ví dụ truyền tham số trong mlflow_tracker.py, dag.py và client.py. Được xem là hoàn tất khi có một cách được ghi lại tài liệu để các hàm node truy cập metadata và context do hook cung cấp, kèm phạm vi kiểm thử cho luồng async từ hook đến node rồi đến hook.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
I'd like to be able to access node metadata (task_id, node_name, and other arbitrary parameters) from within my node functions. My particular use case is writing an async compatible MlFlowTracker plugin. (I have had intermittent sucess with the the mlflow Fluent API trace decorator and would rather use the more explicit client API.)
From within a node function I'd like a way to access context passed in from plugin hooks.
Approach 1 - contextvars
Working with @elijahbenizzy, I tried contextvars but found that the context wasn't maintained between the pre_node_execute hook, node function and the post_node_execute hook. See reproduction here: https://github.com/dmexs/hamilton-contextvar-repro
Approach 2 - modifying kwargs and parameter drilling
Instead I took the approach of injecting a context object into node kwargs which works but requires parameter/prop drilling which I don't love. I'd prefer to be able to retrieve passed data from a contextvar anywhere within the node function execution stack. In my case I'm retrieving my mlflow span/request ids to create subspans on API calls.
Here's what I have for approach #2 which works:
mlflow_tracker.py
from hamilton.lifecycle import base
from hamilton import node, graph
import logging
from typing import Type, Any, Optional, Dict, List
import mlflow
import pickle
import contextvars
from pydantic import BaseModel
from contextlib import asynccontextmanager
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
class NodeContext(BaseModel):
mlflow_request_id: Optional[str] = None
mlflow_root_span_id: Optional[str] = None
class MLFlowTrackerAsync(
base.BasePreNodeExecuteAsync,
base.BasePostNodeExecuteAsync,
base.BasePreGraphExecuteAsync,
base.BasePostGraphExecuteAsync,
):
mlflow_run_info = None
mlflow_client: Optional[mlflow.MlflowClient] = None
mlflow_request_id: Optional[str] = None
mlflow_root_span_id: Optional[str] = None
mlflow_node_span_id_cache = {}
def __init__(self, *args, **kwargs):
if mlflow.active_run():
self.mlflow_client = mlflow.MlflowClient()
self.mlflow_run_info = mlflow.active_run().info
logger.debug(f"MLFlowTrackerAsync: {self.mlflow_run_info.run_id}")
async def pre_graph_execute(
self,
*,
run_id: str,
graph: "graph.FunctionGraph",
final_vars: List[str],
inputs: Dict[str, Any],
overrides: Dict[str, Any],
):
if self.mlflow_run_info:
trace = self.mlflow_client.start_trace(
name=f'hamilton',
inputs=inputs,
experiment_id=self.mlflow_run_info.experiment_id,
span_type=mlflow.entities.SpanType.CHAIN,
)
self.mlflow_request_id = trace.request_id
self.mlflow_root_span_id = trace.span_id
async def post_graph_execute(
self,
*,
run_id: str,
graph: "graph.FunctionGraph",
success: bool,
error: Optional[Exception],
results: Optional[Dict[str, Any]],
):
if self.mlflow_request_id:
self.mlflow_client.end_trace(
request_id=self.mlflow_request_id,
outputs=results
)
async def pre_node_execute(
self,
run_id: str,
node_: node.Node,
kwargs: Dict[str, Any],
task_id: Optional[str] = None
) -> None:
context = kwargs.get('__context', NodeContext())
if self.mlflow_request_id and self.mlflow_root_span_id:
span = self.mlflow_client.start_span(
name=node_.name,
span_type=mlflow.entities.SpanType.CHAIN,
request_id=self.mlflow_request_id,
parent_id=self.mlflow_root_span_id,
inputs=kwargs,
)
self.mlflow_node_span_id_cache[task_id] = span.span_id
context.mlflow_request_id = span.request_id
context.mlflow_root_span_id = span.span_id
if '__context' in node_.input_types:
kwargs['__context'] = context
async def post_node_execute(
self,
run_id: str,
node_: node.Node,
success: bool,
error: Optional[Exception],
result: Any,
task_id: Optional[str] = None,
**future_kwargs: dict,
) -> None:
if self.mlflow_node_span_id_cache.get(task_id, None):
self.mlflow_client.end_span(
request_id=self.mlflow_request_id,
span_id=self.mlflow_node_span_id_cache[task_id],
outputs=result
)
dag.py
async def patient_fhir_id(hero_api: hero_api_async.HeroAPI, uid: str, __context: Optional[NodeContext] = None) -> str:
"""
Lookup patient
"""
async with api as client:
patient = await client.request(endpoint='fhir/patient', params={"id": uid, "idType": "UID"}, context=__context)
fhir_id = patient['id']
return fhir_id
client.py
async def request(
self,
endpoint,
method = 'GET',
params: Optional[dict] = None,
context: Optional[NodeContext] = None
):
span = None
return_response = None
if context and context.mlflow_request_id and context.mlflow_root_span_id:
span = self.mlflow_client.start_span(
name='API.request',
span_type=mlflow.entities.SpanType.RETRIEVER,
request_id=context.mlflow_request_id,
parent_id=context.mlflow_root_span_id,
inputs={
"method": method,
"endpoint": endpoint,
"params": params
}
)
try:
url = self.BASE_URL + endpoint
async with self.session.request(
method=method,
url=url,
params=params,
) as response:
response.raise_for_status() # Raise an HTTPError for bad responses
return_response = await response.json(content_type=None) # Return the response data as a dictionary
return return_response
except Exception as e:
logger.error(f"{method} {response.status} {url} - {e}")
return_response = e
raise e
finally:
if span:
self.mlflow_client.end_span(
request_id=span.request_id,
span_id=span.span_id,
outputs=return_response
)
- Ngôn ngữ chính
- Jupyter Notebook
- Star
- 2.6k
- Fork
- 221
- Merge trung bình
- 2 ngày 1 phút
- Pull request đã merge (30 ngày)
- 17
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của apache/hamilton
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
Maintainer thường phản hồi trong vòng 2 ngày
-
Engine crashes when client sends nan or inf float to UICó thể làm lại được @Exudev đã nhận 234 ngày trước và không có pull request nào đang mở. Đang mởbug SDK
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 65/100
apache/hamilton#1458 · 5 bình luận ·
Maintainer thường phản hồi trong vòng 2 ngày
-
More detailed type annotations in graph visualizationsCó thể làm lại được @Nishant-Manocha đã nhận 369 ngày trước và không có pull request nào đang mở. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
apache/hamilton#1383 · 4 bình luận ·
Maintainer thường phản hồi trong vòng 2 ngày
-
Documentation: add explainer for FastAPICó thể làm lại được @Aditya-Deshwal23 đã nhận 189 ngày trước và không có pull request nào đang mở. Đang mởdocumentation
Độ khó 2/5 1-2 ngày Mức phù hợp với người mới 65/100
apache/hamilton#1304 · 5 bình luận ·
Maintainer thường phản hồi trong vòng 2 ngày
-
Link back to github from reference API docsCó thể đã có người làm @naikvaib đã nhận 26 ngày trước. Đang mởdocumentation good first issue
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
apache/hamilton#572 · 3 bình luận ·
Maintainer thường phản hồi trong vòng 2 ngày
Tất cả issue của apache/hamilton
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
apache/rocketmq-dashboard#6896 ·
Maintainer thường phản hồi trong vòng 3 ngày
-
bug good first issue
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 74/100
-
data
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 74/100
gaseous-project/hasheous#637 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Grouping VCF export never writes the link INFO field (second link() replaces it)Có thể đã có người làm Có pull request liên kết đang mở hoặc đã được merge. Đang mởbug classification
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 90/100
SACGF/variantgrid#2259 ·
Maintainer thường phản hồi trong vòng 1 ngày