Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updating langfuse integration #345

Merged
merged 22 commits into from
Aug 21, 2024
Merged

Updating langfuse integration #345

merged 22 commits into from
Aug 21, 2024

Conversation

kongzii
Copy link
Contributor

@kongzii kongzii commented Aug 13, 2024

No description provided.

Copy link

coderabbitai bot commented Aug 13, 2024

Important

Review skipped

Review was skipped due to path filters

Files ignored due to path filters (2)
  • poetry.lock is excluded by !**/*.lock, !**/*.lock
  • pyproject.toml is excluded by !**/*.toml

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The recent updates significantly enhance the integration of Langfuse within the prediction market agent tooling. Key changes include modifications to type annotations for better flexibility, the introduction of new methods and properties to support conditional Langfuse integration, and improved observability of agent activities and market processing. These changes streamline functionality, allowing easier configuration management and monitoring across various modules.

Changes

Files Change Summary
prediction_market_agent_tooling/config.py Type annotation for LANGFUSE_PUBLIC_KEY changed from Optional[SecretStr] to Optional[str]. Added method default_enable_langfuse to check Langfuse key presence.
prediction_market_agent_tooling/deploy/agent.py Enhanced DeployableAgent and DeployableTraderAgent with new properties and methods for Langfuse integration. Renamed process_bets to process_markets. Introduced new classes ProcessedMarket, AnsweredEnum, and additional methods for improved tracking.
prediction_market_agent_tooling/markets/categorize.py Updated infer_category to include enable_langfuse. Introduced infer_category_observed to leverage Langfuse capabilities by default.
prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py Added @observe() decorator to rewrite_question_into_image_generation_prompt to enhance monitoring.
prediction_market_agent_tooling/tools/is_predictable.py Updated is_predictable_binary and is_predictable_without_description to support enable_langfuse. Introduced observed wrapper functions.
prediction_market_agent_tooling/tools/langfuse_.py New file added with observe function for enhanced monitoring within Langfuse.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Agent
    participant Langfuse
    User->>Agent: Initialize with enable_langfuse
    Agent->>Langfuse: Check keys (PUBLIC, SECRET, HOST)
    Langfuse-->>Agent: Return status (enabled/disabled)
    Agent->>User: Return initialization status
Loading
sequenceDiagram
    participant User
    participant ImageGen
    participant Langfuse
    User->>ImageGen: Request image generation with question
    ImageGen->>Langfuse: Utilize langfuse if enabled
    Langfuse-->>ImageGen: Provide context or logging
    ImageGen->>User: Return generated image
Loading

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@kongzii kongzii marked this pull request as ready for review August 15, 2024 10:34
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Outside diff range, codebase verification and nitpick comments (1)
prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py (1)

Line range hint 8-30:
Enhance ImportError handling.

The function correctly integrates Langfuse conditionally, and the try-except block is a good practice. However, consider using raise ... from None to clarify the source of the ImportError.

-    except ImportError:
+    except ImportError as e:
+        raise ImportError(
+            "openai not installed, please install extras `langchain` to use this function."
+        ) from None
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 77b55dd and c43d0cd.

Files ignored due to path filters (3)
  • mypy.ini is excluded by !**/*.ini
  • poetry.lock is excluded by !**/*.lock, !**/*.lock
  • pyproject.toml is excluded by !**/*.toml
Files selected for processing (7)
  • prediction_market_agent_tooling/config.py (3 hunks)
  • prediction_market_agent_tooling/deploy/agent.py (7 hunks)
  • prediction_market_agent_tooling/markets/categorize.py (3 hunks)
  • prediction_market_agent_tooling/monitor/monitor.py (1 hunks)
  • prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py (2 hunks)
  • prediction_market_agent_tooling/tools/is_predictable.py (4 hunks)
  • prediction_market_agent_tooling/tools/langfuse_.py (1 hunks)
Files skipped from review due to trivial changes (1)
  • prediction_market_agent_tooling/monitor/monitor.py
Additional context used
Ruff
prediction_market_agent_tooling/markets/categorize.py

17-19: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

prediction_market_agent_tooling/deploy/agent.py

356-359: Return the condition is_predictable_binary_observed(market.question) directly

Replace with return is_predictable_binary_observed(market.question)

(SIM103)

Additional comments not posted (20)
prediction_market_agent_tooling/tools/langfuse_.py (1)

12-26: LGTM!

The observe function is well-implemented, providing a flexible wrapper around original_observe with clear type annotations and customizable parameters.

prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py (2)

36-39: LGTM!

The addition of the enable_langfuse parameter enhances the flexibility of the generate_image_for_market function.


43-45: LGTM!

The generate_image_for_market_observed function is well-implemented, leveraging the observe decorator to enable monitoring.

prediction_market_agent_tooling/markets/categorize.py (1)

51-57: LGTM!

The infer_category_observed function is well-implemented, leveraging the observe decorator to enable monitoring.

prediction_market_agent_tooling/config.py (3)

138-144: The addition of default_enable_langfuse is well-implemented.

This property method provides a clear and efficient way to determine if Langfuse integration should be enabled based on key presence.


127-129: Verify the impact of changing the return type of langfuse_public_key to str.

The return type change may affect how the public key is used in the codebase. Ensure that all usages are compatible with this change.

Verification successful

The change to langfuse_public_key return type is compatible with its usage.

The method langfuse_public_key is used directly as a public_key argument without any secret-specific operations, confirming that changing the return type to str does not introduce any issues.

  • File: prediction_market_agent_tooling/deploy/agent.py
    • Line: public_key=keys.langfuse_public_key
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of `langfuse_public_key` to ensure compatibility with the new return type.

# Test: Search for the usage of `langfuse_public_key`. Expect: Compatibility with `str` type.
rg --type python -A 5 $'langfuse_public_key'

Length of output: 74


Script:

#!/bin/bash
# Description: Verify the usage of `langfuse_public_key` to ensure compatibility with the new return type.

# Test: Search for the usage of `langfuse_public_key`. Expect: Compatibility with `str` type.
rg -A 5 'langfuse_public_key'

Length of output: 965


43-43: Consider the security implications of changing LANGFUSE_PUBLIC_KEY to str.

The change from SecretStr to str may reduce the security of handling the public key as it is no longer treated as a secret. Ensure that this change aligns with your security requirements.

Verification successful

Verify security requirements for LANGFUSE_PUBLIC_KEY handling.

The LANGFUSE_PUBLIC_KEY is not treated as sensitive data in the current codebase, which might justify changing its type from SecretStr to str. Ensure that this change aligns with your security policies and requirements.

  • LANGFUSE_PUBLIC_KEY is used in prediction_market_agent_tooling/config.py without special handling for sensitive data.
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of `LANGFUSE_PUBLIC_KEY` to ensure it aligns with security requirements.

# Test: Search for the usage of `LANGFUSE_PUBLIC_KEY`. Expect: Proper handling as non-sensitive data.
rg --type python -A 5 $'LANGFUSE_PUBLIC_KEY'

Length of output: 74


Script:

#!/bin/bash
# Description: Verify the usage of `LANGFUSE_PUBLIC_KEY` to ensure it aligns with security requirements.

# Test: Search for the usage of `LANGFUSE_PUBLIC_KEY`. Expect: Proper handling as non-sensitive data.
rg --type py -A 5 $'LANGFUSE_PUBLIC_KEY'

Length of output: 1371

prediction_market_agent_tooling/tools/is_predictable.py (4)

110-124: The addition of is_predictable_binary_observed is well-implemented.

This function offers a straightforward way to observe predictions with Langfuse enabled, enhancing observability.


167-183: The addition of is_predictable_without_description_observed is well-implemented.

This function offers a straightforward way to observe predictions with Langfuse enabled, enhancing observability.


Line range hint 81-105: The addition of enable_langfuse in is_predictable_binary is well-implemented.

This change allows for optional Langfuse integration, enhancing the function's flexibility.

Ensure that all calls to is_predictable_binary are updated to accommodate the new parameter.


Line range hint 133-162: The addition of enable_langfuse in is_predictable_without_description is well-implemented.

This change allows for optional Langfuse integration, enhancing the function's flexibility.

Ensure that all calls to is_predictable_without_description are updated to accommodate the new parameter.

prediction_market_agent_tooling/deploy/agent.py (9)

116-125: The addition of initialize_langfuse is well-implemented.

This method provides a centralized setup for Langfuse integration, enhancing maintainability.


127-156: The addition of langfuse_update_current_trace is well-implemented.

This method provides flexibility in updating trace information with Langfuse, enhancing observability.


287-298: The addition of update_langfuse_trace_by_market is well-implemented.

This method provides a clear way to update Langfuse trace information based on market data, enhancing observability.


300-313: The addition of update_langfuse_trace_by_processed_market is well-implemented.

This method provides a structured approach to updating Langfuse trace information after market processing, enhancing observability.


408-444: The addition of process_market is well-implemented.

This method integrates market processing with Langfuse observability enhancements, improving traceability.


468-486: The refactoring of process_bets to process_markets is well-implemented.

This change aligns with the new processing logic, enhancing clarity and maintainability.


328-372: The observability enhancements using @observe are well-implemented.

The use of the @observe decorator enhances the monitoring of agent interactions, providing valuable insights.

Also applies to: 397-403

Tools
Ruff

356-359: Return the condition is_predictable_binary_observed(market.question) directly

Replace with return is_predictable_binary_observed(market.question)

(SIM103)


107-115: The addition of enable_langfuse in DeployableAgent is well-implemented.

This change enhances the configurability of the agent with Langfuse integration.

Ensure that all instantiations of DeployableAgent are updated to accommodate the new parameter.


279-284: The addition of enable_langfuse in DeployableTraderAgent is well-implemented.

This change enhances the configurability of the trader agent with Langfuse integration.

Ensure that all instantiations of DeployableTraderAgent are updated to accommodate the new parameter.

prediction_market_agent_tooling/markets/categorize.py Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between c43d0cd and 900e59b.

Files ignored due to path filters (1)
  • poetry.lock is excluded by !**/*.lock, !**/*.lock
Files selected for processing (1)
  • prediction_market_agent_tooling/deploy/agent.py (7 hunks)
Additional context used
Ruff
prediction_market_agent_tooling/deploy/agent.py

359-362: Return the condition is_predictable_binary_observed(market.question) directly

Replace with return is_predictable_binary_observed(market.question)

(SIM103)

Additional comments not posted (5)
prediction_market_agent_tooling/deploy/agent.py (5)

96-98: LGTM!

The ProcessedMarket class is well-defined and uses Pydantic for data validation.


101-103: LGTM!

The AnsweredEnum class provides clear and concise states for market answers.


107-114: LGTM!

The constructor of DeployableAgent effectively integrates Langfuse with conditional initialization.


116-128: LGTM!

The initialize_langfuse method is well-structured and handles configuration based on the enable_langfuse flag.


130-159: LGTM!

The langfuse_update_current_trace method effectively updates the Langfuse trace with relevant metadata and provides useful default arguments.

prediction_market_agent_tooling/deploy/agent.py Outdated Show resolved Hide resolved
LANGFUSE_HOST: t.Optional[str] = None
LANGFUSE_DEPLOYMENT_VERSION: t.Optional[str] = None
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a new env variable that is populated by git commit sha in PMA's docker building pipeline; it will show us, in the UI, what version of PMA was used.

@@ -134,6 +135,14 @@ def langfuse_host(self) -> str:
self.LANGFUSE_HOST, "LANGFUSE_HOST missing in the environment."
)

@property
def default_enable_langfuse(self) -> bool:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used as the default argument in DeployableAgent init method

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and self.LANGFUSE_DEPLOYMENT_VERSION is not None?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not necessary for langfuse to work, it can be None

"""
Provide some useful default arguments when updating the current trace in our agents.
"""
langfuse_context.update_current_trace(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a wrapper around update_current_trace, see the default arguments below. The reason is to always include these in our PMAT-based agents.

def have_bet_on_market_since_observed(
self, market: AgentMarket, since: timedelta
) -> bool:
return self.have_bet_on_market_since(market, since)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the pattern I was talking about last time, I don't like it so much but I also don't see any clearer way to proceed.

This pattern is used on all observed methods of DeployableAgent.

The reason is, that if some method is @observed() and the agent class needs to implement that method (answer_binary_market is a clear example, but I think it will be usable for others as well), then the overridden method would not be @observed() anymore.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love this pattern either, this means every time you add a new method, you need to add an observed one (to avoid people overriding the observed one and only overriding the "pure" one).

One solution I came across is adding a class decorator to DeployableAgent, which "observes" the a set of functions without annotating them. Hence you could avoid having to manually decorate them, instead only decorating the base class (see snippet below).
What this would imply:
-> Annotate DeployableAgent with decorator, define that methods [have_bet_on_market, ...] should be observed. Add logic to observe function (e.g. call decorator(my_function) or similar)
-> If subclass (e.g. CoinflipDeployableAgent) overwrites the method, it doesn't matter, it will keep getting observed (due to class decorator being in the base class).
-> This is nice because you avoid having duplicate methods everywhere.

Let me know what you think @kongzii - Gist here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That could work, but based on the gist, it would decorate all the methods, which is not needed (and maybe including methods as __init__ and similar?)

So we would need something like

@prepend_foo_to_methods(allowed_methods=["have_bet_on_market", ...])
class MyClass:

which isn't type-safe and can be easily forgotten.

Another approach I was experimenting with was something like

def initialize_langfuse(self) -> None:
    ...
    self.have_bet_on_market = observe()(self.have_bet_on_market)  # type: ignore[method-assign]

It works similarly to your suggestion, but mypy should warn us if for example have_bet_on_market gets deleted or something (but I didn't test it), although it still needs type: ignore comment, because changing methods like this isn't allowed.

I didn't use that approach because it needs type: ignore, but I don't feel strongly about it.

Do you have preferences?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@prepend_foo_to_methods(allowed_methods=["have_bet_on_market", ...])
class MyClass:

What I had in mind was:
-> Add the decorator to the base class (DeployableTraderAgent). Note that subclasses (e.g. DeployableCoinFlipTraderAgent) and similar do not get decorated. Hence we need to decorate only once (or maybe more, but only on base classes), so not very hard to remember.
-> The decorator does not modify all functions, only a subset (either hardcoded or even better, as you suggested it, provided as an argument).
-> Not sure how to satisfy mypy here, maybe using a FrozenSet but not sure

Regarding the other approach (overriding methods with observe()(self.have_bet_on_market) # type: ignore[method-assign], it's a similar approach to the decorator, but I like the decorator a bit better because I find it more elegant (no real reason).

The point I'm trying to make is: I don't love the duplicated-function pattern (observed + pure). If we can find any solution to this (be it decorator or non-decorator), I'm happy with any option.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I don't care for duplicated *_observer method or doing some magic, but what I'm afraid of is that

@magic(["predict"])
class Agent:
    def predictt(self):
        print("Predicting")

will never trigger mypy as a mistake, but

from prediction_market_agent_tooling.tools.langfuse_ import observe

class Agent:
    def init_langfuse(self) -> None:
        self.predict = observe()(self.predict)

    def predictt(self) -> None:
        print("Predicting")

will. And it also works with the auto-refactoring features of VSCode.

In any case, we will get rid of _observed methods, I'll change it.

return have_bet_on_market_since(keys=APIKeys(), market=market, since=since)

@observe()
def verify_market_observed(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to refactor the logic of these methods, before it was:

  1. Fetch markets
  2. Filter markets
  3. Predict+Bet on them one by one

Now it is

  1. Fetch markets
  2. One by one check if the market is ok to predict and if so, predict + bet on it.

The reason is that we need the whole process end-to-end saved in Langfuse, this is how it looks like:

Screenshot by Dropbox Capture

) -> t.Sequence[AgentMarket]:
return random.sample(markets, 1)
def verify_market(self, market_type: MarketType, market: AgentMarket) -> bool:
return True
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After refactoring, it's no longer possible to randomly pick one of the markets, but hopefully, that's not a problem.

@@ -17,6 +26,10 @@ def infer_category(
"""
)

config: RunnableConfig = {}
if enable_langfuse:
config["callbacks"] = [langfuse_context.get_current_langchain_handler()]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is again a pattern that I don't like so much, but it's required.

One would probably expect a solution as:

def infer_category(
    ...,
    callbacks: list[...] | None = None,
):

And then simply use the callbacks in the invoke method.

However, when langfuse_context.get_current_langchain_handler() is called, it will pick up the latest observed function (see in the previous screenshot that observation can be nested), and because of that, we need to initialize this callback right before calling the invoke method, because otherwise, it could be linked to a different observation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I will think some more about this, if you don't have some clear refactor idea for this, I'd merge it and change in another PR if I think of something 🙏

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issued at #346

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you at least turn this into a util function, like:

def get_langchain_runnable_config(enable_langfuse: bool) -> RunnableConfig:
    config: RunnableConfig = {}
    if enable_langfuse:
        config["callbacks"] = [langfuse_context.get_current_langchain_handler()]
    return config

?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that's what you propose as (1) in #346, except existing_config is always None in PMAT.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason not to do that? Just to save lines I guess

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, I can do it. I just also need to think about the suggestion below. But now I'm just responding in-between time when I don't know what to write into the paper 😄

I'll look in more detail into all comments tomorrow 👌

capture_output: bool = True,
transform_to_string: Optional[Callable[[Iterable[Any]], str]] = None,
) -> Callable[[Callable[P, R]], Callable[P, R]]:
casted: Callable[[Callable[P, R]], Callable[P, R]] = original_observe(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is described at langfuse/langfuse-python#862

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we also override their decorator to avoid having to have this pattern

@observe
def foo_observed()
    return foo(enable_langfuse=True)

by doing something like:

from prediction_market_agent.utils import APIKeys

class APIKeysTest(APIKeys):
    FOO: str | None = None
    @property
    def default_enable_langfuse(self):
        return self.FOO is not None

def original_observe(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result + "foo"
    return wrapper

def observe(func):
    def noop_decorator(func):
        return func

    enable = APIKeysTest().default_enable_langfuse
    if enable:
        return original_observe(func)
    else:
        return noop_decorator(func)

@observe
def hello_world():
    return "Hello, world! "

print(hello_world())

i.e. you get

%  python test.py
Hello, world! 
%  FOO=1 python test.py
Hello, world! foo

The drawback to this is that now you can't do:

agent0 = DeployableFooAgent(enable_langfuse=True)
agent1 = DeployableFooAgent(enable_langfuse=False)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's a good idea to add additional logic into their decorator, because what can happen is that someone uses both PMAT and also observes some of his method manually (like we do in PMA), and then they could receive different behaviours on observed methods.

However, it seems like this is fine even without modifying the observer decorator:

Screenshot by Dropbox Capture

So we can just @observe everything by default. I thought it's a good idea to have both observed and non-observed functions, but now I don't feel strong about it.

And in combination with #345 (comment), code will be simpler to follow.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

someone uses both PMAT and also observes some of his method manually (like we do in PMA), and then they could receive different behaviours on observed methods.

Sorry I don't understand this bit.

So we can just @observe everything by default.

But anyway, this sounds good

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 900e59b and 4eff02e.

Files selected for processing (1)
  • prediction_market_agent_tooling/deploy/agent_example.py (2 hunks)

Comment on lines +13 to +14
def verify_market(self, market_type: MarketType, market: AgentMarket) -> bool:
return True
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider implementing verification logic in verify_market.

The verify_market method currently acts as a placeholder by always returning True. If market verification is intended to be a part of the agent's functionality, consider implementing the necessary logic to validate the market_type and market.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 4eff02e and 572021e.

Files selected for processing (5)
  • prediction_market_agent_tooling/deploy/agent.py (7 hunks)
  • prediction_market_agent_tooling/markets/categorize.py (2 hunks)
  • prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py (2 hunks)
  • prediction_market_agent_tooling/tools/is_predictable.py (4 hunks)
  • prediction_market_agent_tooling/tools/langfuse_.py (1 hunks)
Files skipped from review as they are similar to previous changes (2)
  • prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py
  • prediction_market_agent_tooling/tools/langfuse_.py
Additional context used
Ruff
prediction_market_agent_tooling/markets/categorize.py

19-21: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

prediction_market_agent_tooling/deploy/agent.py

354-357: Return the condition is_predictable_binary(market.question) directly

Replace with return is_predictable_binary(market.question)

(SIM103)

Additional comments not posted (7)
prediction_market_agent_tooling/markets/categorize.py (2)

8-8: infer_category_observed function looks good.

The function correctly wraps infer_category to enable Langfuse by default.


Line range hint 14-42:
LGTM! But verify the function usage in the codebase.

The code changes are approved.

However, ensure that all function calls to infer_category match the new signature.

Verification successful

No usage of infer_category found in the codebase. The infer_category function is defined but not called anywhere else in the current codebase. No further verification of function calls is required.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify all function calls to `infer_category` match the new signature.

# Test: Search for the function usage. Expect: Only occurrences of the new signature.
rg --type python -A 5 $'infer_category'

Length of output: 69


Script:

#!/bin/bash
# Correcting the file type specification and searching for the function definition and usage of `infer_category`.

# Search for the definition of `infer_category` in Python files
rg --type py -A 5 'def infer_category'

# Search for the usage of `infer_category` in Python files
rg --type py -A 5 'infer_category('

Length of output: 606


Script:

#!/bin/bash
# Correcting the regex pattern by escaping the parenthesis and searching for the usage of `infer_category`.

# Search for the usage of `infer_category` in Python files
rg --type py -A 5 'infer_category\('

Length of output: 485

Tools
Ruff

19-21: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

prediction_market_agent_tooling/tools/is_predictable.py (2)

Line range hint 79-108:
Integration with Langfuse looks good.

The function correctly integrates Langfuse capabilities for enhanced functionality.


Line range hint 114-149:
Integration with Langfuse looks good.

The function correctly integrates Langfuse capabilities for enhanced functionality.

prediction_market_agent_tooling/deploy/agent.py (3)

105-157: Langfuse integration in DeployableAgent looks good.

The class correctly integrates Langfuse for enhanced monitoring capabilities.


Line range hint 280-427:
Enhancements in DeployableTraderAgent look good.

The refactoring and new methods significantly improve functionality and observability.


450-475: Verify the impact of method renaming on the codebase.

Ensure that all references to renamed methods are updated accordingly.

Verification successful

Method Renaming Verified Successfully

The codebase does not contain any references to the old method names process_bets, before_process_bets, or after_process_bets. This indicates that all references have been updated accordingly.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify all references to renamed methods are updated.

# Test: Search for the old method names. Expect: No occurrences of old method names.
rg --type python -A 5 $'process_bets|before|after'

Length of output: 82


Script:

#!/bin/bash
# Description: Verify all references to renamed methods are updated.

# Test: Search for the old method names. Expect: No occurrences of old method names.
rg --type py -A 5 'process_bets|before_process_bets|after_process_bets'

Length of output: 73

Comment on lines +19 to +21
raise ImportError(
"openai not installed, please install extras `langchain` to use this function."
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enhance ImportError handling.

Consider using raise ... from None to clarify the source of the ImportError.

-    except ImportError:
+    except ImportError as e:
+        raise ImportError(
+            "openai not installed, please install extras `langchain` to use this function."
+        ) from None
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
raise ImportError(
"openai not installed, please install extras `langchain` to use this function."
)
except ImportError as e:
raise ImportError(
"openai not installed, please install extras `langchain` to use this function."
) from None
Tools
Ruff

19-21: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

Comment on lines +145 to +149
completion = str(
llm(
messages, max_tokens=max_tokens, config=get_langfuse_langchain_config()
).content
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplify the return statement.

The return statement can be simplified by directly returning the condition.

-    completion = str(
-        llm(
-            messages, max_tokens=max_tokens, config=get_langfuse_langchain_config()
-        ).content
-    )
+    completion = llm(
+        messages, max_tokens=max_tokens, config=get_langfuse_langchain_config()
+    ).content
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
completion = str(
llm(
messages, max_tokens=max_tokens, config=get_langfuse_langchain_config()
).content
)
completion = llm(
messages, max_tokens=max_tokens, config=get_langfuse_langchain_config()
).content

Comment on lines +104 to +108
completion = str(
llm.invoke(
messages, max_tokens=max_tokens, config=get_langfuse_langchain_config()
).content
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplify the return statement.

The return statement can be simplified by directly returning the condition.

-    completion = str(
-        llm.invoke(
-            messages, max_tokens=max_tokens, config=get_langfuse_langchain_config()
-        ).content
-    )
+    completion = llm.invoke(
+        messages, max_tokens=max_tokens, config=get_langfuse_langchain_config()
+    ).content
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
completion = str(
llm.invoke(
messages, max_tokens=max_tokens, config=get_langfuse_langchain_config()
).content
)
completion = llm.invoke(
messages, max_tokens=max_tokens, config=get_langfuse_langchain_config()
).content

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 572021e and 041ac59.

Files ignored due to path filters (2)
  • poetry.lock is excluded by !**/*.lock, !**/*.lock
  • pyproject.toml is excluded by !**/*.toml
Files selected for processing (1)
  • prediction_market_agent_tooling/config.py (3 hunks)
Files skipped from review as they are similar to previous changes (1)
  • prediction_market_agent_tooling/config.py

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 041ac59 and 78935d0.

Files selected for processing (1)
  • prediction_market_agent_tooling/deploy/agent.py (8 hunks)
Additional context used
Ruff
prediction_market_agent_tooling/deploy/agent.py

358-361: Return the condition is_predictable_binary(market.question) directly

Replace with return is_predictable_binary(market.question)

(SIM103)

Additional comments not posted (11)
prediction_market_agent_tooling/deploy/agent.py (11)

1-11: Imports look good.

The import statements are appropriate and necessary for the file's functionality.


76-88: Langfuse initialization is well-implemented.

The initialize_langfuse function correctly configures Langfuse based on the enable_langfuse flag.


109-117: New class and enum are well-structured.

The ProcessedMarket class and AnsweredEnum enum enhance the structure and readability of market processing logic.


120-131: Constructor changes enhance configurability.

The inclusion of the enable_langfuse parameter and the call to initialize_langfuse improve flexibility and ensure proper initialization.


132-161: Trace update method is well-implemented.

The langfuse_update_current_trace method is well-documented and provides useful defaults for updating traces.


284-289: Constructor changes ensure proper initialization.

The addition of the enable_langfuse parameter and the call to the superclass constructor enhance configurability and ensure proper initialization.


292-299: Automated observability is well-implemented.

The use of the observe decorator to automatically observe methods reduces manual effort and enhances observability.


301-327: Trace update methods enhance traceability.

The update_langfuse_trace_by_market and update_langfuse_trace_by_processed_market methods are well-structured and improve traceability of market interactions.


394-430: Market processing method is comprehensive.

The process_market method effectively integrates Langfuse tracking and ensures comprehensive market processing.


454-471: Multiple market processing is well-structured.

The process_markets method ensures appropriate balance checks and processes multiple markets efficiently.


477-479: Market processing flow is well-orchestrated.

The run method provides a clear and organized flow for processing markets.

Comment on lines +358 to +361
if not is_predictable_binary(market.question):
return False

return picked
return True
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplify the return statement.

The return statement can be simplified by directly returning the condition.

- if not is_predictable_binary(market.question):
-     return False
- return True
+ return is_predictable_binary(market.question)
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if not is_predictable_binary(market.question):
return False
return picked
return True
return is_predictable_binary(market.question)
Tools
Ruff

358-361: Return the condition is_predictable_binary(market.question) directly

Replace with return is_predictable_binary(market.question)

(SIM103)

@evangriffiths evangriffiths self-requested a review August 20, 2024 09:47
def initialize_langfuse(self) -> None:
super().initialize_langfuse()
# Auto-observe all the methods where it makes sense, so that subclassses don't need to do it manually.
self.have_bet_on_market_since = observe()(self.have_bet_on_market_since) # type: ignore[method-assign]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason you do @observe() in some places, but observe()(self.some_class_method) here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, if something is just a function:

@observe()
def research(...) -> ...

We can use the decorator, and no harm is done. Anyone who uses research will use the observed variant.

But in our agent:

class Agent:
   @observe()
   def xyz(...) -> <default strategy>

class ProphetAgent(Agent):
   def xyz(...) -> <some more fancy strategy> <<<--- This would remove the decorator, so all deployable agent would have to manually @observe their methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is related to #345 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah makes sense, thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants