Tru Custom App¶
Custom class Apps¶
This wrapper covers apps that are not based on one of the high-level frameworks such as langchain or llama-index. We instead assume that some python class or classes implements an app which has similar functionality to LLM apps coded in the high-level frameworks in that it generally processes text queries to produce text outputs while making intermediate queries to things like LLMs, vector DBs, and similar.
Example Usage¶
Consider a mock question-answering app with a context retriever component coded
up as two classes in two python, CustomApp
and CustomRetriever
:
custom_app.py
¶
from trulens_eval.tru_custom_app import instrument
from custom_retriever import CustomRetriever
class CustomApp:
# NOTE: No restriction on this class.
def __init__(self):
self.retriever = CustomRetriever()
@instrument
def retrieve_chunks(self, data):
return self.retriever.retrieve_chunks(data)
@instrument
def respond_to_query(self, input):
chunks = self.retrieve_chunks(input) output = f"The answer to {input} is
probably {chunks[0]} or something ..." return output
custom_retriever.py
¶
from trulens_eval.tru_custom_app import instrument
class CustomRetriever:
# NOTE: No restriction on this class either.
@instrument
def retrieve_chunks(self, data):
return [
f"Relevant chunk: {data.upper()}", f"Relevant chunk: {data[::-1]}"
]
The core tool for instrumenting these classes is the instrument
method
(actually class, but details are not important here). trulens needs to be aware
of two high-level concepts to usefully monitor the app: components and methods
used by components. The instrument
must decorate each method that the user
wishes to watch (for it to show up on the dashboard). In the example, all of the
functionalities are decorated. Additionally, the owner classes of any decorated
method is viewed as an app component. In this case CustomApp
and
CustomRetriever
are components.
Following the instrumentation, the app can be used with or without tracking:
example.py
¶
from custom_app import CustomApp from trulens_eval.tru_custom_app
import TruCustomApp
ca = CustomApp()
# Normal app Usage:
response = ca.respond_to_query("What is the capital of Indonesia?")
# Wrapping app with `TruCustomApp`:
ta = TruCustomApp(ca)
# Wrapped Usage: must use the general `with_record` (or `awith_record`) method:
response, record = ta.with_record(
ca.respond_to_query, input="What is the capital of Indonesia?"
)
The with_record
use above returns both the response of the app normally
produces as well as the record of the app as is the case with the higher-level
wrappers. TruCustomApp
constructor arguments are like in those higher-level
apps as well including the feedback functions, metadata, etc.
Instrumenting 3rd party classes¶
In cases you do not have access to a class to make the necessary decorations for
tracking, you can instead use one of the static methods of instrument
, for
example, the alterative for making sure the custom retriever gets instrumented
is via:
# custom_app.py`:
from trulens_eval.tru_custom_app import instrument
from somepackage.from custom_retriever import CustomRetriever
instrument.method(CustomRetriever, "retrieve_chunks")
# ... rest of the custom class follows ...
API Usage Tracking¶
Uses of python libraries for common LLMs like OpenAI are tracked in custom class apps.
Covered LLM Libraries¶
- Official OpenAI python package (https://github.com/openai/openai-python).
Huggingface¶
Uses of huggingface inference APIs are tracked as long as requests are made
through the requests
class's post
method to the URL
https://api-inference.huggingface.co .
Limitations¶
-
Tracked (instrumented) components must be accessible through other tracked components. Specifically, an app cannot have a custom class that is not instrumented but that contains an instrumented class. The inner instrumented class will not be found by trulens.
-
All tracked components are categorized as "Custom" (as opposed to Template, LLM, etc.). That is, there is no categorization available for custom components. They will all show up as "uncategorized" in the dashboard.
-
Non json-like contents of components (that themselves are not components) are not recorded or available in dashboard. This can be alleviated to some extent with the
app_extra_json
argument toTruCustomClass
as it allows one to specify in the form of json additional information to store alongside the component hierarchy. Json-like (json bases like string, int, and containers like sequences and dicts are included).
What can go wrong¶
- If a
with_record
orawith_record
call does not encounter any instrumented method, it will raise an error. You can check which methods are instrumented usingApp.print_instrumented
. You may have forgotten to decorate relevant methods with@instrument
.
app.print_instrumented()
### output example:
Components:
Custom of trulens_eval.app component: *.__app__.app
Custom of trulens_eval.app component: *.__app__.app.llm
Custom of trulens_eval.app component: *.__app__.app.retriever
Custom of trulens_eval.app component: *.__app__.app.template
Methods:
Object at 0x1064c6d30:
<function CustomApp.retrieve_chunks at 0x14c6c5700> with path *.__app__.app
<function CustomApp.respond_to_query at 0x14c6c5790> with path *.__app__.app
Object at 0x1064c6dc0:
<function CustomLLM.generate at 0x14c6c51f0> with path *.__app__.app.llm
Object at 0x1064c6f70:
<function CustomRetriever.retrieve_chunks at 0x14c6b5c10> with path *.__app__.app.retriever
Object at 0x106272100:
<function CustomTemplate.fill at 0x14c6c54c0> with path *.__app__.app.template
- If an instrumented / decorated method's owner object cannot be found when
traversing your custom class, you will get a warning. This may be ok in the
end but may be indicative of a problem. Specifically, note the "Tracked"
limitation above. You can also use the
app_extra_json
argument toApp
/TruCustomApp
to provide a structure to stand in place for (or augment) the data produced by walking over instrumented components to make sure this hierarchy contains the owner of each instrumented method.
The owner-not-found error looks like this:
Function <function CustomRetriever.retrieve_chunks at 0x177935d30> was not found during instrumentation walk. Make sure it is accessible by traversing app <custom_app.CustomApp object at 0x112a005b0> or provide a bound method for it as TruCustomApp constructor argument `methods_to_instrument`.
Function <function CustomTemplate.fill at 0x1779474c0> was not found during instrumentation walk. Make sure it is accessible by traversing app <custom_app.CustomApp object at 0x112a005b0> or provide a bound method for it as TruCustomApp constructor argument `methods_to_instrument`.
Function <function CustomLLM.generate at 0x1779471f0> was not found during instrumentation walk. Make sure it is accessible by traversing app <custom_app.CustomApp object at 0x112a005b0> or provide a bound method for it as TruCustomApp constructor argument `methods_to_instrument`.
Subsequent attempts at with_record
/awith_record
may result in the "Empty
record" exception.
- Usage tracking not tracking. We presently have limited coverage over which
APIs we track and make some assumptions with regards to accessible APIs
through lower-level interfaces. Specifically, we only instrument the
requests
module'spost
method for the lower level tracking. Please file an issue on github with your use cases so we can work out a more complete solution as needed.
TruCustomApp
¶
Bases: App
Instantiates a Custom App that can be tracked as long as methods are decorated with @instrument.
Usage:
from trulens_eval import instrument
class CustomApp:
def __init__(self):
self.retriever = CustomRetriever()
self.llm = CustomLLM()
self.template = CustomTemplate(
"The answer to {question} is probably {answer} or something ..."
)
@instrument
def retrieve_chunks(self, data):
return self.retriever.retrieve_chunks(data)
@instrument
def respond_to_query(self, input):
chunks = self.retrieve_chunks(input)
answer = self.llm.generate(",".join(chunks))
output = self.template.fill(question=input, answer=answer)
return output
ca = CustomApp()
from trulens_eval import TruCustomApp
# f_lang_match, f_qa_relevance, f_qs_relevance are feedback functions
tru_recorder = TruCustomApp(ca,
app_id="Custom Application v1",
feedbacks=[f_lang_match, f_qa_relevance, f_qs_relevance])
question = "What is the capital of Indonesia?"
# Normal Usage:
response_normal = ca.respond_to_query(question)
# Instrumented Usage:
with tru_recorder as recording:
ca.respond_to_query(question)
tru_record = recording.records[0]
# To add record metadata
with tru_recorder as recording:
recording.record_metadata="this is metadata for all records in this context that follow this line"
ca.respond_to_query("What is llama 2?")
recording.record_metadata="this is different metadata for all records in this context that follow this line"
ca.respond_to_query("Where do I download llama 2?")
Parameters:
Name | Type | Description | Default |
---|---|---|---|
app |
Any
|
Any class |
required |
Source code in trulens_eval/trulens_eval/tru_custom_app.py
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 |
|
__init__(app, methods_to_instrument=None, **kwargs)
¶
Wrap a custom class for recording.
- app: Any -- the custom app object being wrapped.
- More args in App
- More args in AppDefinition
- More args in WithClassInfo
Source code in trulens_eval/trulens_eval/tru_custom_app.py
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 |
|
instrument
¶
Bases: base_instrument
Decorator for marking methods to be instrumented in custom classes that are wrapped by TruCustomApp.
Source code in trulens_eval/trulens_eval/tru_custom_app.py
436 437 438 439 440 441 442 443 444 445 446 447 448 |
|