|
1 | 1 | # Inspecting logs |
2 | 2 |
|
3 | | -All `gd.Guard` calls are logged internally, and can be accessed via two methods, `gd.Guard.guard_state` or `guardrails.log`. |
| 3 | +All `Guard` calls are logged internally, and can be accessed via the guard history. |
4 | 4 |
|
5 | | -## 🪵 Accessing logs via `guardrails.log` |
| 5 | +## 🇻🇦 Accessing logs via `Guard.history` |
6 | 6 |
|
7 | | -This is the simplest way to access logs. It returns a list of all `gd.Guard` calls, in the order they were made. |
| 7 | +`history` is an attribute of the `Guard` class. It implements a standard `Stack` interface with a few extra helper methods and properties. For more information on our `Stack` implementation see the [Helper Classes](/api_reference/helper_classes) page. |
8 | 8 |
|
9 | | -In order to access logs, run: |
| 9 | +Each entry in the history stack is a `Call` log which will contain information specific to a particular `Guard.__call__` or `Guard.parse` call in the order that they were executed within the current session. |
10 | 10 |
|
11 | | -```bash |
12 | | - |
13 | | -eliot-tree --output-format=ascii guardrails.log |
| 11 | +For example, if you have a guard: |
14 | 12 |
|
| 13 | +```py |
| 14 | +my_guard = Guard.from_rail(...) |
15 | 15 | ``` |
16 | 16 |
|
17 | | -## 🇻🇦 Accessing logs via `gd.Guard.guard_state` |
| 17 | +and you call it multiple times: |
| 18 | + |
| 19 | +```py |
| 20 | +response_1 = my_guard(...) |
18 | 21 |
|
19 | | -`guard_state` is an attribute of the `gd.Guard` class. It contains: |
| 22 | +response_2 = my_guard.parse(...) |
| 23 | +``` |
20 | 24 |
|
21 | | -1. A list of all `gd.Guard` calls, in the order they were made. |
22 | | -2. For each call, reasks needed and their results. |
| 25 | +Then `guard.history` will have two call logs with the first representing the first call `response_1 = my_guard(...)` and the second representing the following `parse` call `response_2 = my_guard.parse(...)`. |
23 | 26 |
|
24 | | -To pretty print logs, run: |
| 27 | +To pretty print logs for the latest call, run: |
25 | 28 |
|
26 | 29 | ```python |
27 | 30 | from rich import print |
28 | 31 |
|
29 | | -print(guard.state.most_recent_call.tree) |
| 32 | +print(guard.history.last.tree) |
30 | 33 | ``` |
| 34 | +--8<-- |
31 | 35 |
|
32 | | - |
| 36 | +docs/html/single-step-history.html |
33 | 37 |
|
34 | | -To access fine-grained logs on field validation, see the FieldValidationLogs object: |
| 38 | +--8<-- |
35 | 39 |
|
36 | | -```python |
37 | | -validation_logs = guard.guard_state.all_histories[0].history[0].field_validation_logs |
38 | | -print(validation_logs.json(indent=2)) |
| 40 | +The `Call` log will contain initial and final information about a particular guard call. |
| 41 | + |
| 42 | +```py |
| 43 | +first_call = my_guard.history.first |
| 44 | +``` |
| 45 | + |
| 46 | +For example, it tracks the initial inputs as provided: |
| 47 | +```py |
| 48 | +print("prompt\n-----") |
| 49 | +print(first_call.prompt) |
| 50 | +print("prompt params\n------------- ") |
| 51 | +print(first_call.prompt_params) |
| 52 | +``` |
| 53 | +```log |
| 54 | +prompt |
| 55 | +----- |
| 56 | +
|
| 57 | +You are a human in an enchanted forest. You come across opponents of different types. You should fight smaller opponents, run away from bigger ones, and freeze if the opponent is a bear. |
| 58 | +
|
| 59 | +You run into a ${opp_type}. What do you do? |
| 60 | +
|
| 61 | +${gr.complete_json_suffix_v2} |
| 62 | +
|
| 63 | +
|
| 64 | +Here are a few examples |
| 65 | +
|
| 66 | +goblin: {"action": {"chosen_action": "fight", "weapon": "crossbow"}} |
| 67 | +troll: {"action": {"chosen_action": "fight", "weapon": "sword"}} |
| 68 | +giant: {"action": {"chosen_action": "flight", "flight_direction": "north", "distance": 1}} |
| 69 | +dragon: {"action": {"chosen_action": "flight", "flight_direction": "south", "distance": 4}} |
| 70 | +black bear: {"action": {"chosen_action": "freeze", "duration": 3}} |
| 71 | +beets: {"action": {"chosen_action": "fight", "weapon": "fork"}} |
| 72 | +
|
| 73 | +prompt params |
| 74 | +------------- |
| 75 | +{'opp_type': 'grizzly'} |
39 | 76 | ``` |
40 | 77 |
|
41 | | -```json |
| 78 | +as well as the final outputs: |
| 79 | +```py |
| 80 | +print("status: ", first_call.status) # The final status of this guard call |
| 81 | +print("validated response:", first_call.validated_output) # The final valid output of this guard call |
| 82 | +``` |
| 83 | +```log |
| 84 | +status: pass |
| 85 | +validated response: {'action': {'chosen_action': 'freeze', 'duration': 3}} |
| 86 | +``` |
| 87 | + |
| 88 | + |
| 89 | +The `Call` log also tracks cumulative values from any iterations that happen within the call. |
| 90 | + |
| 91 | +For example, if the first response from the LLM fails validation and a reask occurs, the `Call` log can provide total tokens consumed (*currently only for OpenAI models), as well as access to all of the raw outputs from the LLM: |
| 92 | +```py |
| 93 | +print("prompt token usage: ", first_call.prompt_tokens_consumed) # Total number of prompt tokens consumed across iterations within this call |
| 94 | +print("completion token usage: ", first_call.completion_tokens_consumed) # Total number of completion tokens consumed across iterations within this call |
| 95 | +print("total token usage: ",first_call.tokens_consumed) # Total number of tokens consumed; equal to the sum of the two values above |
| 96 | +print("llm responses\n-------------") # An Stack of the LLM responses in order that they were received |
| 97 | +for r in first_call.raw_outputs: |
| 98 | + print(r) |
| 99 | +``` |
| 100 | +```log |
| 101 | +prompt token usage: 909 |
| 102 | +completion token usage: 57 |
| 103 | +total token usage: 966 |
| 104 | +
|
| 105 | +llm responses |
| 106 | +------------- |
| 107 | +{"action": {"chosen_action": "freeze"}} |
42 | 108 | { |
43 | | - "validator_logs": [], |
44 | | - "children": { |
45 | | - "name": { |
46 | | - "validator_logs": [ |
47 | | - { |
48 | | - "validator_name": "TwoWords", |
49 | | - "value_before_validation": "peter parker the second", |
50 | | - "validation_result": { |
51 | | - "outcome": "fail", |
52 | | - "metadata": null, |
53 | | - "error_message": "must be exactly two words", |
54 | | - "fix_value": "peter parker" |
55 | | - }, |
56 | | - "value_after_validation": { |
57 | | - "incorrect_value": "peter parker the second", |
58 | | - "fail_results": [ |
59 | | - { |
60 | | - "outcome": "fail", |
61 | | - "metadata": null, |
62 | | - "error_message": "must be exactly two words", |
63 | | - "fix_value": "peter parker" |
64 | | - } |
65 | | - ], |
66 | | - "path": [ |
67 | | - "name" |
68 | | - ] |
69 | | - } |
70 | | - } |
71 | | - ], |
72 | | - "children": {} |
73 | | - } |
| 109 | + "action": { |
| 110 | + "chosen_action": "freeze", |
| 111 | + "duration": null |
74 | 112 | } |
75 | 113 | } |
| 114 | +{ |
| 115 | + "action": { |
| 116 | + "chosen_action": "freeze", |
| 117 | + "duration": 1 |
| 118 | + } |
| 119 | +} |
| 120 | +``` |
| 121 | + |
| 122 | +For more information on `Call`, see the [History & Logs](/api_reference/history_and_logs/#guardrails.classes.history.Call) page. |
| 123 | + |
| 124 | +## 🇻🇦 Accessing logs from individual steps |
| 125 | +In addition to the cumulative values available directly on the `Call` log, it also contains a `Stack` of `Iteration`'s. Each `Iteration` represent the logs from within a step in the guardrails process. This includes the call to the LLM, as well as parsing and validating the LLM's response. |
| 126 | + |
| 127 | +Each `Iteration` is treated as a stateless entity so it will only contain information about the inputs and outputs of the particular step it represents. |
| 128 | + |
| 129 | +For example, in order to see the raw LLM response as well as the logs for the specific validations that failed during the first step of a call, we can access this information via that steps `Iteration`: |
| 130 | + |
| 131 | +```py |
| 132 | +first_step = first_call.iterations.first |
76 | 133 |
|
| 134 | +first_llm_output = first_step.raw_output |
| 135 | +print("First LLM response\n------------------") |
| 136 | +print(first_llm_output) |
| 137 | +print(" ") |
| 138 | + |
| 139 | +validation_logs = first_step.validator_logs |
| 140 | +print("\nValidator Logs\n--------------") |
| 141 | +for log in validation_logs: |
| 142 | + print(log.json(indent=2)) |
| 143 | +``` |
| 144 | +```log |
| 145 | +First LLM response |
| 146 | +------------------ |
| 147 | +{"action": {"chosen_action": "fight", "weapon": "spoon"}} |
| 148 | + |
| 149 | +
|
| 150 | +Validator Logs |
| 151 | +-------------- |
| 152 | +{ |
| 153 | + "validator_name": "ValidChoices", |
| 154 | + "value_before_validation": "spoon", |
| 155 | + "validation_result": { |
| 156 | + "outcome": "fail", |
| 157 | + "metadata": null, |
| 158 | + "error_message": "Value spoon is not in choices ['crossbow', 'axe', 'sword', 'fork'].", |
| 159 | + "fix_value": null |
| 160 | + }, |
| 161 | + "value_after_validation": { |
| 162 | + "incorrect_value": "spoon", |
| 163 | + "fail_results": [ |
| 164 | + { |
| 165 | + "outcome": "fail", |
| 166 | + "metadata": null, |
| 167 | + "error_message": "Value spoon is not in choices ['crossbow', 'axe', 'sword', 'fork'].", |
| 168 | + "fix_value": null |
| 169 | + } |
| 170 | + ], |
| 171 | + "path": [ |
| 172 | + "action", |
| 173 | + "weapon" |
| 174 | + ] |
| 175 | + } |
| 176 | +} |
| 177 | +``` |
| 178 | + |
| 179 | +Similar to the `Call` log, we can also see the token usage for just this step: |
| 180 | +```py |
| 181 | +print("prompt token usage: ", first_step.prompt_tokens_consumed) |
| 182 | +print("completion token usage: ", first_step.completion_tokens_consumed) |
| 183 | +print("token usage for this step: ",first_step.tokens_consumed) |
| 184 | +``` |
| 185 | +```log |
| 186 | +prompt token usage: 617 |
| 187 | +completion token usage: 16 |
| 188 | +token usage for this step: 633 |
| 189 | +``` |
77 | 190 |
|
78 | | -``` |
| 191 | +For more information on the properties available on `Iteration`, ee the [History & Logs](/api_reference/history_and_logs/#guardrails.classes.history.Iteration) page. |
0 commit comments