flowstate-sdk 0.1.11__tar.gz → 0.1.13__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.
- {flowstate_sdk-0.1.11/src/flowstate_sdk.egg-info → flowstate_sdk-0.1.13}/PKG-INFO +1 -1
- {flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13}/pyproject.toml +1 -1
- {flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13}/src/flowstate_sdk/context.py +3 -0
- flowstate_sdk-0.1.13/src/flowstate_sdk/langchain/__init__.py +15 -0
- flowstate_sdk-0.1.13/src/flowstate_sdk/langchain/callback_handler.py +3 -0
- flowstate_sdk-0.1.13/src/flowstate_sdk/langchain/chat_models.py +91 -0
- flowstate_sdk-0.1.13/src/flowstate_sdk/langchain/flowstate_chat_base.py +51 -0
- flowstate_sdk-0.1.11/src/flowstate_sdk/langchain/callback_handler.py → flowstate_sdk-0.1.13/src/flowstate_sdk/langchain/telemetry.py +48 -18
- {flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13}/src/flowstate_sdk/workflow.py +7 -0
- {flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13/src/flowstate_sdk.egg-info}/PKG-INFO +1 -1
- {flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13}/src/flowstate_sdk.egg-info/SOURCES.txt +5 -1
- {flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13}/LICENSE +0 -0
- {flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13}/README.md +0 -0
- {flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13}/setup.cfg +0 -0
- {flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13}/src/flowstate_sdk/__init__.py +0 -0
- {flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13}/src/flowstate_sdk/constants.py +0 -0
- {flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13}/src/flowstate_sdk/cost_table.py +0 -0
- {flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13}/src/flowstate_sdk/decorators.py +0 -0
- {flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13}/src/flowstate_sdk/enums.py +0 -0
- {flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13}/src/flowstate_sdk/sdk_client.py +0 -0
- {flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13}/src/flowstate_sdk/shared_dataclasses.py +0 -0
- {flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13}/src/flowstate_sdk/task.py +0 -0
- {flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13}/src/flowstate_sdk/task_context.py +0 -0
- {flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13}/src/flowstate_sdk.egg-info/dependency_links.txt +0 -0
- {flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13}/src/flowstate_sdk.egg-info/requires.txt +0 -0
- {flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13}/src/flowstate_sdk.egg-info/top_level.txt +0 -0
|
@@ -7,3 +7,6 @@ run_stack: ContextVar[List[str]] = ContextVar("run_stack", default=[])
|
|
|
7
7
|
parent_children_mappings: ContextVar[Dict[str, Set[str]]] = ContextVar(
|
|
8
8
|
"parent_children_mappings", default=defaultdict(set)
|
|
9
9
|
)
|
|
10
|
+
is_replay: ContextVar[bool] = ContextVar("is_replay", default=False)
|
|
11
|
+
replay_counter: ContextVar[int] = ContextVar("replay_counter", default=0)
|
|
12
|
+
replay_step_list: ContextVar[List[str]] = ContextVar("replay_step_list", default=[])
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from flowstate_sdk.langchain.callback_handler import FlowstateCallbackHandler
|
|
2
|
+
from flowstate_sdk.langchain.chat_models import (
|
|
3
|
+
FlowstateChatAnthropic,
|
|
4
|
+
FlowstateChatClaude,
|
|
5
|
+
FlowstateChatGoogle,
|
|
6
|
+
FlowstateChatOpenAI,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"FlowstateCallbackHandler",
|
|
11
|
+
"FlowstateChatOpenAI",
|
|
12
|
+
"FlowstateChatAnthropic",
|
|
13
|
+
"FlowstateChatClaude",
|
|
14
|
+
"FlowstateChatGoogle",
|
|
15
|
+
]
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from flowstate_sdk.langchain.flowstate_chat_base import FlowstateChatModelMixin
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from langchain_openai import ChatOpenAI
|
|
9
|
+
except Exception: # pragma: no cover - optional dependency
|
|
10
|
+
ChatOpenAI = None
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
from langchain_anthropic import ChatAnthropic
|
|
14
|
+
except Exception: # pragma: no cover - optional dependency
|
|
15
|
+
ChatAnthropic = None
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
|
19
|
+
except Exception: # pragma: no cover - optional dependency
|
|
20
|
+
ChatGoogleGenerativeAI = None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _missing_dependency(name: str, package: str) -> None:
|
|
24
|
+
raise ImportError(
|
|
25
|
+
f"{name} requires optional dependency '{package}'. "
|
|
26
|
+
f"Install it to use this Flowstate wrapper."
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if ChatOpenAI is not None:
|
|
31
|
+
|
|
32
|
+
class FlowstateChatOpenAI(FlowstateChatModelMixin, ChatOpenAI):
|
|
33
|
+
provider = "openai"
|
|
34
|
+
|
|
35
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
36
|
+
kwargs = self._flowstate_init_kwargs(kwargs)
|
|
37
|
+
super().__init__(*args, **kwargs)
|
|
38
|
+
|
|
39
|
+
else:
|
|
40
|
+
|
|
41
|
+
class FlowstateChatOpenAI(FlowstateChatModelMixin): # type: ignore[misc]
|
|
42
|
+
provider = "openai"
|
|
43
|
+
|
|
44
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
45
|
+
_missing_dependency("FlowstateChatOpenAI", "langchain-openai")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
if ChatAnthropic is not None:
|
|
49
|
+
|
|
50
|
+
class FlowstateChatAnthropic(FlowstateChatModelMixin, ChatAnthropic):
|
|
51
|
+
provider = "anthropic"
|
|
52
|
+
|
|
53
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
54
|
+
kwargs = self._flowstate_init_kwargs(kwargs)
|
|
55
|
+
super().__init__(*args, **kwargs)
|
|
56
|
+
|
|
57
|
+
else:
|
|
58
|
+
|
|
59
|
+
class FlowstateChatAnthropic(FlowstateChatModelMixin): # type: ignore[misc]
|
|
60
|
+
provider = "anthropic"
|
|
61
|
+
|
|
62
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
63
|
+
_missing_dependency("FlowstateChatAnthropic", "langchain-anthropic")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
if ChatGoogleGenerativeAI is not None:
|
|
67
|
+
|
|
68
|
+
class FlowstateChatGoogle(FlowstateChatModelMixin, ChatGoogleGenerativeAI):
|
|
69
|
+
provider = "google"
|
|
70
|
+
|
|
71
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
72
|
+
kwargs = self._flowstate_init_kwargs(kwargs)
|
|
73
|
+
super().__init__(*args, **kwargs)
|
|
74
|
+
|
|
75
|
+
else:
|
|
76
|
+
|
|
77
|
+
class FlowstateChatGoogle(FlowstateChatModelMixin): # type: ignore[misc]
|
|
78
|
+
provider = "google"
|
|
79
|
+
|
|
80
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
81
|
+
_missing_dependency("FlowstateChatGoogle", "langchain-google-genai")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
FlowstateChatClaude = FlowstateChatAnthropic
|
|
85
|
+
|
|
86
|
+
__all__ = [
|
|
87
|
+
"FlowstateChatOpenAI",
|
|
88
|
+
"FlowstateChatAnthropic",
|
|
89
|
+
"FlowstateChatClaude",
|
|
90
|
+
"FlowstateChatGoogle",
|
|
91
|
+
]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Iterable, List, Optional
|
|
4
|
+
|
|
5
|
+
from flowstate_sdk.langchain.telemetry import FlowstateCallbackHandler
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _extract_model_name(kwargs: Dict[str, Any]) -> Optional[str]:
|
|
9
|
+
for key in ("model", "model_name", "model_id"):
|
|
10
|
+
val = kwargs.get(key)
|
|
11
|
+
if isinstance(val, str) and val.strip():
|
|
12
|
+
return val
|
|
13
|
+
return None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _normalize_callbacks(callbacks: Any) -> List[Any]:
|
|
17
|
+
if callbacks is None:
|
|
18
|
+
return []
|
|
19
|
+
if isinstance(callbacks, list):
|
|
20
|
+
return list(callbacks)
|
|
21
|
+
if isinstance(callbacks, tuple):
|
|
22
|
+
return list(callbacks)
|
|
23
|
+
return [callbacks]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _has_flowstate_callback(callbacks: Iterable[Any]) -> bool:
|
|
27
|
+
for cb in callbacks:
|
|
28
|
+
if isinstance(cb, FlowstateCallbackHandler):
|
|
29
|
+
return True
|
|
30
|
+
return False
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _merge_callbacks(existing: Any, additional: List[Any]) -> List[Any]:
|
|
34
|
+
existing_list = _normalize_callbacks(existing)
|
|
35
|
+
if _has_flowstate_callback(existing_list):
|
|
36
|
+
return existing_list
|
|
37
|
+
return existing_list + list(additional)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class FlowstateChatModelMixin:
|
|
41
|
+
provider: str = ""
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def _flowstate_build_callbacks(cls, kwargs: Dict[str, Any]) -> List[Any]:
|
|
45
|
+
model = _extract_model_name(kwargs) or "unknown"
|
|
46
|
+
handler = FlowstateCallbackHandler(cls.provider, model)
|
|
47
|
+
return _merge_callbacks(kwargs.pop("callbacks", None), [handler])
|
|
48
|
+
|
|
49
|
+
def _flowstate_init_kwargs(self, kwargs: Dict[str, Any]) -> Dict[str, Any]:
|
|
50
|
+
kwargs["callbacks"] = self._flowstate_build_callbacks(kwargs)
|
|
51
|
+
return kwargs
|
|
@@ -252,6 +252,31 @@ def _is_tool_call_message(message: Any, gen_info: Optional[Dict[str, Any]]) -> b
|
|
|
252
252
|
return False
|
|
253
253
|
|
|
254
254
|
|
|
255
|
+
def _estimate_token_count(text: Optional[str], model: Optional[str]) -> Optional[int]:
|
|
256
|
+
if not text:
|
|
257
|
+
return 0
|
|
258
|
+
try:
|
|
259
|
+
import tiktoken # type: ignore
|
|
260
|
+
except Exception:
|
|
261
|
+
tiktoken = None
|
|
262
|
+
|
|
263
|
+
if tiktoken is not None:
|
|
264
|
+
try:
|
|
265
|
+
try:
|
|
266
|
+
enc = tiktoken.encoding_for_model(model or "")
|
|
267
|
+
except Exception:
|
|
268
|
+
enc = tiktoken.get_encoding("cl100k_base")
|
|
269
|
+
return len(enc.encode(text))
|
|
270
|
+
except Exception:
|
|
271
|
+
return None
|
|
272
|
+
|
|
273
|
+
if os.getenv("FLOWSTATE_APPROX_TOKENS"):
|
|
274
|
+
approx = (len(text) + 3) // 4
|
|
275
|
+
return approx if approx > 0 else 0
|
|
276
|
+
|
|
277
|
+
return None
|
|
278
|
+
|
|
279
|
+
|
|
255
280
|
class FlowstateCallbackHandler(BaseCallbackHandler):
|
|
256
281
|
def __init__(self, provider: str, model: str) -> None:
|
|
257
282
|
self.provider = provider
|
|
@@ -341,6 +366,25 @@ class FlowstateCallbackHandler(BaseCallbackHandler):
|
|
|
341
366
|
if finish_reason is None and _is_tool_call_message(ai_message_chunk, gen_info):
|
|
342
367
|
return
|
|
343
368
|
|
|
369
|
+
llm_output = getattr(response, "llm_output", None)
|
|
370
|
+
resolved_model = self.model
|
|
371
|
+
if isinstance(llm_output, dict):
|
|
372
|
+
for key in ("model_name", "model", "model_id"):
|
|
373
|
+
val = llm_output.get(key)
|
|
374
|
+
if isinstance(val, str) and val.strip():
|
|
375
|
+
resolved_model = val
|
|
376
|
+
break
|
|
377
|
+
resolved_model = _normalize_model_name(resolved_model)
|
|
378
|
+
|
|
379
|
+
# Extract output text robustly (chat message content or plain .text)
|
|
380
|
+
output_text = ""
|
|
381
|
+
if response.generations and response.generations[0]:
|
|
382
|
+
generation = response.generations[0][0]
|
|
383
|
+
if getattr(generation, "message", None) is not None:
|
|
384
|
+
output_text = _content_to_text(generation.message.content)
|
|
385
|
+
else:
|
|
386
|
+
output_text = generation.text or ""
|
|
387
|
+
|
|
344
388
|
usage_candidates: List[Dict[str, Any]] = []
|
|
345
389
|
gen_response_metadata = getattr(generation_chunk, "response_metadata", None)
|
|
346
390
|
if isinstance(gen_response_metadata, dict):
|
|
@@ -360,20 +404,15 @@ class FlowstateCallbackHandler(BaseCallbackHandler):
|
|
|
360
404
|
usage_candidates.append(additional_kwargs)
|
|
361
405
|
if isinstance(gen_info, dict) and gen_info:
|
|
362
406
|
usage_candidates.append(gen_info)
|
|
363
|
-
llm_output = getattr(response, "llm_output", None)
|
|
364
407
|
if isinstance(llm_output, dict):
|
|
365
408
|
usage_candidates.append(llm_output)
|
|
366
409
|
|
|
367
410
|
input_tokens, output_tokens = _extract_token_usage(usage_candidates)
|
|
411
|
+
if input_tokens is None:
|
|
412
|
+
input_tokens = _estimate_token_count(self._input_str, resolved_model)
|
|
413
|
+
if output_tokens is None:
|
|
414
|
+
output_tokens = _estimate_token_count(output_text, resolved_model)
|
|
368
415
|
|
|
369
|
-
resolved_model = self.model
|
|
370
|
-
if isinstance(llm_output, dict):
|
|
371
|
-
for key in ("model_name", "model", "model_id"):
|
|
372
|
-
val = llm_output.get(key)
|
|
373
|
-
if isinstance(val, str) and val.strip():
|
|
374
|
-
resolved_model = val
|
|
375
|
-
break
|
|
376
|
-
resolved_model = _normalize_model_name(resolved_model)
|
|
377
416
|
if os.getenv("FLOWSTATE_DEBUG_LLM_USAGE"):
|
|
378
417
|
try:
|
|
379
418
|
import json
|
|
@@ -410,15 +449,6 @@ class FlowstateCallbackHandler(BaseCallbackHandler):
|
|
|
410
449
|
if cost_value != 0.0:
|
|
411
450
|
cost_usd = cost_value
|
|
412
451
|
|
|
413
|
-
# Extract output text robustly (chat message content or plain .text)
|
|
414
|
-
output_text = ""
|
|
415
|
-
if response.generations and response.generations[0]:
|
|
416
|
-
generation = response.generations[0][0]
|
|
417
|
-
if getattr(generation, "message", None) is not None:
|
|
418
|
-
output_text = _content_to_text(generation.message.content)
|
|
419
|
-
else:
|
|
420
|
-
output_text = generation.text or ""
|
|
421
|
-
|
|
422
452
|
provider_metrics = ProviderMetrics(
|
|
423
453
|
run_id=context.current_run.get(),
|
|
424
454
|
provider=self.provider,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import uuid
|
|
2
2
|
from collections import defaultdict
|
|
3
3
|
from datetime import datetime
|
|
4
|
+
import os
|
|
4
5
|
|
|
5
6
|
from . import context
|
|
6
7
|
from .sdk_client import SDKClient
|
|
@@ -40,6 +41,12 @@ class Workflow:
|
|
|
40
41
|
}
|
|
41
42
|
self.client.emit(workflow_payload)
|
|
42
43
|
|
|
44
|
+
if os.getenv("IS_FLOWSTATE_REPLAY"):
|
|
45
|
+
replay_list = self.client.retrieve_replay_plan(os.getenv("FLOWSTATE_REPLAY_PARENT_ID"))
|
|
46
|
+
context.replay_counter.set(0)
|
|
47
|
+
context.replay_step_list.set(replay_list)
|
|
48
|
+
context.is_replay.set(True)
|
|
49
|
+
|
|
43
50
|
run_payload = RunRecord(
|
|
44
51
|
run_id=str(self.run_id),
|
|
45
52
|
workflow_id=str(self.workflow_id),
|
|
@@ -17,4 +17,8 @@ src/flowstate_sdk.egg-info/SOURCES.txt
|
|
|
17
17
|
src/flowstate_sdk.egg-info/dependency_links.txt
|
|
18
18
|
src/flowstate_sdk.egg-info/requires.txt
|
|
19
19
|
src/flowstate_sdk.egg-info/top_level.txt
|
|
20
|
-
src/flowstate_sdk/langchain/
|
|
20
|
+
src/flowstate_sdk/langchain/__init__.py
|
|
21
|
+
src/flowstate_sdk/langchain/callback_handler.py
|
|
22
|
+
src/flowstate_sdk/langchain/chat_models.py
|
|
23
|
+
src/flowstate_sdk/langchain/flowstate_chat_base.py
|
|
24
|
+
src/flowstate_sdk/langchain/telemetry.py
|
|
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
|
{flowstate_sdk-0.1.11 → flowstate_sdk-0.1.13}/src/flowstate_sdk.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|