mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-09 08:08:32 +00:00
Lorenze/ensure hooks work with lite agents flows (#3981)
* liteagent support hooks * wip llm.call hooks work - needs tests for this * fix tests * fixed more * more tool hooks test cassettes
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: '{"messages":[{"role":"user","content":"Say hello"}],"model":"gpt-4o-mini"}'
|
||||
headers:
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate, zstd
|
||||
authorization:
|
||||
- AUTHORIZATION-XXX
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '74'
|
||||
content-type:
|
||||
- application/json
|
||||
host:
|
||||
- api.openai.com
|
||||
user-agent:
|
||||
- OpenAI/Python 1.109.1
|
||||
x-stainless-arch:
|
||||
- arm64
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- MacOS
|
||||
x-stainless-package-version:
|
||||
- 1.109.1
|
||||
x-stainless-read-timeout:
|
||||
- '600'
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.13.3
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
H4sIAAAAAAAAAwAAAP//jFJNb9QwEL3nVww+b9Am7Ee7FyT2wCIQ0CJxqarItSdZg+Ox7AmwVPvf
|
||||
KyftJv1A4uLDvHnP783MbQYgjBYbEGovWbXe5tvm6/bv5ZeDu5AmlubTzr///G778fKi+O6/iVli
|
||||
0M0PVPzAeq2o9RbZkBtgFVAyJtVivVqUZbku3vRASxptojWe8wXlrXEmL+flIp+v8+Lsnr0nozCK
|
||||
DVxlAAC3/Zt8Oo1/xAbms4dKizHKBsXm1AQgAtlUETJGE1k6FrMRVOQYXW99h9bSK9jRb1DSwQcY
|
||||
CHCgDpi0PLydEgPWXZTJvOusnQDSOWKZwveWr++R48mkpcYHuolPqKI2zsR9FVBGcslQZPKiR48Z
|
||||
wHU/jO5RPuEDtZ4rpp/Yf3c+qIlxA88xJpZ2LBdnsxe0Ko0sjY2TUQol1R71yBznLjttaAJkk8TP
|
||||
vbykPaQ2rvkf+RFQCj2jrnxAbdTjvGNbwHSe/2o7Tbg3LCKGX0ZhxQZD2oLGWnZ2OBoRD5GxrWrj
|
||||
Ggw+mOFyal8tV3NZr3C5PBfZMbsDAAD//wMARXm1qUcDAAA=
|
||||
headers:
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Thu, 27 Nov 2025 05:51:54 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Strict-Transport-Security:
|
||||
- max-age=31536000; includeSubDomains; preload
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
access-control-expose-headers:
|
||||
- X-Request-ID
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
cf-cache-status:
|
||||
- DYNAMIC
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
x-openai-proxy-wasm:
|
||||
- v0.1
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -0,0 +1,87 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: '{"messages":[{"role":"system","content":"You are Test Assistant. You are
|
||||
a helpful test assistant\nYour personal goal is: Answer questions briefly\n\nTo
|
||||
give my best complete final answer to the task respond using the exact following
|
||||
format:\n\nThought: I now can give a great answer\nFinal Answer: Your final
|
||||
answer must be the great and the most complete as possible, it must be outcome
|
||||
described.\n\nI MUST use these formats, my job depends on it!"},{"role":"user","content":"Say
|
||||
''Hello World'' and nothing else"}],"model":"gpt-4.1-mini"}'
|
||||
headers:
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate, zstd
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '540'
|
||||
content-type:
|
||||
- application/json
|
||||
host:
|
||||
- api.openai.com
|
||||
user-agent:
|
||||
- OpenAI/Python 1.109.1
|
||||
x-stainless-arch:
|
||||
- arm64
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- MacOS
|
||||
x-stainless-package-version:
|
||||
- 1.109.1
|
||||
x-stainless-read-timeout:
|
||||
- '600'
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.13.3
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
H4sIAAAAAAAAAwAAAP//jFLLbtswELzrKxY8W4HlSjasW5Gibfo6FU1fgUCTK4kuxSVIKm4a+N8L
|
||||
So6ltCnQiwDt7Axndvc+AWBKshKYaHkQndXpZXNVv3vz6cWv68/b/XtX0OHuw9v9l92r/uslZ4vI
|
||||
oN0eRXhgXQjqrMagyIywcMgDRtVss86z7aZYPxuAjiTqSGtsSPOLLO2UUelquSrSZZ5m+YnekhLo
|
||||
WQnfEgCA++EbjRqJP1kJy8VDpUPveYOsPDcBMEc6Vhj3XvnATWCLCRRkAprB+8eW+qYNJVyBoQMI
|
||||
bqBRtwgcmhgAuPEHdN/NS2W4hufDXwmvUWuCa3JaznUd1r3nMZzptZ4B3BgKPA5nSHRzQo7nDJoa
|
||||
62jn/6CyWhnl28oh92SiXx/IsgE9JgA3w6z6R/GZddTZUAX6gcNz2XI16rFpRzO0OIGBAtezerZZ
|
||||
PKFXSQxcaT+bNhNctCgn6rQa3ktFMyCZpf7bzVPaY3Jlmv+RnwAh0AaUlXUolXiceGpzGE/4X23n
|
||||
KQ+GmUd3qwRWQaGLm5BY816Pd8X8nQ/YVbUyDTrr1Hhcta22m/Uai3y7W7HkmPwGAAD//wMABY90
|
||||
7msDAAA=
|
||||
headers:
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 26 Nov 2025 22:52:43 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Strict-Transport-Security:
|
||||
- max-age=31536000; includeSubDomains; preload
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
access-control-expose-headers:
|
||||
- X-Request-ID
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
cf-cache-status:
|
||||
- DYNAMIC
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
x-openai-proxy-wasm:
|
||||
- v0.1
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -0,0 +1,108 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: '{"messages":[{"role":"user","content":"Say hello"}],"model":"gpt-4o-mini"}'
|
||||
headers:
|
||||
User-Agent:
|
||||
- X-USER-AGENT-XXX
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- ACCEPT-ENCODING-XXX
|
||||
authorization:
|
||||
- AUTHORIZATION-XXX
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '74'
|
||||
content-type:
|
||||
- application/json
|
||||
host:
|
||||
- api.openai.com
|
||||
x-stainless-arch:
|
||||
- X-STAINLESS-ARCH-XXX
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- X-STAINLESS-OS-XXX
|
||||
x-stainless-package-version:
|
||||
- 1.83.0
|
||||
x-stainless-read-timeout:
|
||||
- X-STAINLESS-READ-TIMEOUT-XXX
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.13.3
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
H4sIAAAAAAAAAwAAAP//jFKxbtswEN31FVfOViEbRix76RAUcIaiQzu1CASaPMlMSR5Bnhobgf+9
|
||||
oJRYSpMCXTjcu/f43t09FQDCaLEDoY6SlQu2vDXu+5f2HNeeTubuVDl3Wu6/Pn7ufuy/HcQiM+jw
|
||||
gIpfWB8VuWCRDfkRVhElY1Zdbm7Wm7rebG8GwJFGm2ld4HJNpTPelKtqtS6rTbmsn9lHMgqT2MHP
|
||||
AgDgaXizT6/xJHZQLV4qDlOSHYrdtQlARLK5ImRKJrH0LBYTqMgz+sH6Hq2lD7CnR1DSwx2MBDhT
|
||||
D0xanj/NiRHbPsls3vfWzgDpPbHM4QfL98/I5WrSUhciHdJfVNEab9KxiSgT+WwoMQUxoJcC4H4Y
|
||||
Rv8qnwiRXOCG6RcO321HNTFt4C3GxNJO5WW9eEer0cjS2DQbpVBSHVFPzGnusteGZkAxS/zWy3va
|
||||
Y2rju/+RnwClMDDqJkTURr3OO7VFzOf5r7brhAfDImH8bRQ2bDDmLWhsZW/HoxHpnBhd0xrfYQzR
|
||||
jJfThgaVrBTWq20tikvxBwAA//8DAFoGAGtHAwAA
|
||||
headers:
|
||||
CF-RAY:
|
||||
- CF-RAY-XXX
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 03 Dec 2025 19:06:36 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- SET-COOKIE-XXX
|
||||
Strict-Transport-Security:
|
||||
- STS-XXX
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- X-CONTENT-TYPE-XXX
|
||||
access-control-expose-headers:
|
||||
- ACCESS-CONTROL-XXX
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
cf-cache-status:
|
||||
- DYNAMIC
|
||||
openai-organization:
|
||||
- OPENAI-ORG-XXX
|
||||
openai-processing-ms:
|
||||
- '371'
|
||||
openai-project:
|
||||
- OPENAI-PROJECT-XXX
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
x-envoy-upstream-service-time:
|
||||
- '387'
|
||||
x-openai-proxy-wasm:
|
||||
- v0.1
|
||||
x-ratelimit-limit-requests:
|
||||
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||
x-ratelimit-limit-tokens:
|
||||
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||
x-ratelimit-remaining-requests:
|
||||
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||
x-ratelimit-remaining-tokens:
|
||||
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||
x-ratelimit-reset-requests:
|
||||
- X-RATELIMIT-RESET-REQUESTS-XXX
|
||||
x-ratelimit-reset-tokens:
|
||||
- X-RATELIMIT-RESET-TOKENS-XXX
|
||||
x-request-id:
|
||||
- X-REQUEST-ID-XXX
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -0,0 +1,115 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: '{"messages":[{"role":"system","content":"You are Test Assistant. You are
|
||||
a helpful test assistant\nYour personal goal is: Answer questions briefly\n\nTo
|
||||
give my best complete final answer to the task respond using the exact following
|
||||
format:\n\nThought: I now can give a great answer\nFinal Answer: Your final
|
||||
answer must be the great and the most complete as possible, it must be outcome
|
||||
described.\n\nI MUST use these formats, my job depends on it!"},{"role":"user","content":"Say
|
||||
''Hello World'' and nothing else"}],"model":"gpt-4.1-mini"}'
|
||||
headers:
|
||||
User-Agent:
|
||||
- X-USER-AGENT-XXX
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- ACCEPT-ENCODING-XXX
|
||||
authorization:
|
||||
- AUTHORIZATION-XXX
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '540'
|
||||
content-type:
|
||||
- application/json
|
||||
host:
|
||||
- api.openai.com
|
||||
x-stainless-arch:
|
||||
- X-STAINLESS-ARCH-XXX
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- X-STAINLESS-OS-XXX
|
||||
x-stainless-package-version:
|
||||
- 1.83.0
|
||||
x-stainless-read-timeout:
|
||||
- X-STAINLESS-READ-TIMEOUT-XXX
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.13.3
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
H4sIAAAAAAAAAwAAAP//jFLBbtQwEL3nK0Y+b6okZJuSW4VAFIq4rFQkqCLXmSQDjseynRZU7b8j
|
||||
Z7ebFIrEJVLmzXt+b2YeEwBBrahBqEEGNVqdvqFxd331hW+2hD5scyrd5asPxe7j9edPb8UmMvju
|
||||
O6rwxDpTPFqNgdgcYOVQBoyqeXVeVhcX1evtDIzcoo603oa0PMvTkQylRVZs06xM8/JIH5gUelHD
|
||||
1wQA4HH+RqOmxZ+ihmzzVBnRe9mjqE9NAMKxjhUhvScfpAlis4CKTUAze98NPPVDqOEKDD+AkgZ6
|
||||
ukeQ0McAII1/QPfNvCMjNVzOfzW8R60Zbtjpdq3rsJu8jOHMpPUKkMZwkHE4c6LbI7I/ZdDcW8d3
|
||||
/g+q6MiQHxqH0rOJfn1gK2Z0nwDczrOansUX1vFoQxP4B87P5Vlx0BPLjlbo9ggGDlKv6nm1eUGv
|
||||
aTFI0n41baGkGrBdqMtq5NQSr4BklfpvNy9pH5KT6f9HfgGUQhuwbazDltTzxEubw3jC/2o7TXk2
|
||||
LDy6e1LYBEIXN9FiJyd9uCvhf/mAY9OR6dFZR4fj6mxTlFWeqarLzkWyT34DAAD//wMAP95PRmsD
|
||||
AAA=
|
||||
headers:
|
||||
CF-RAY:
|
||||
- CF-RAY-XXX
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 03 Dec 2025 19:06:36 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- SET-COOKIE-XXX
|
||||
Strict-Transport-Security:
|
||||
- STS-XXX
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- X-CONTENT-TYPE-XXX
|
||||
access-control-expose-headers:
|
||||
- ACCESS-CONTROL-XXX
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
cf-cache-status:
|
||||
- DYNAMIC
|
||||
openai-organization:
|
||||
- OPENAI-ORG-XXX
|
||||
openai-processing-ms:
|
||||
- '620'
|
||||
openai-project:
|
||||
- OPENAI-PROJECT-XXX
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
x-envoy-upstream-service-time:
|
||||
- '1891'
|
||||
x-openai-proxy-wasm:
|
||||
- v0.1
|
||||
x-ratelimit-limit-requests:
|
||||
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||
x-ratelimit-limit-tokens:
|
||||
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||
x-ratelimit-remaining-requests:
|
||||
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||
x-ratelimit-remaining-tokens:
|
||||
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||
x-ratelimit-reset-requests:
|
||||
- X-RATELIMIT-RESET-REQUESTS-XXX
|
||||
x-ratelimit-reset-tokens:
|
||||
- X-RATELIMIT-RESET-TOKENS-XXX
|
||||
x-request-id:
|
||||
- X-REQUEST-ID-XXX
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -0,0 +1,486 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: '{"messages":[{"role":"system","content":"You are Calculator Assistant.
|
||||
You are a helpful calculator assistant\nYour personal goal is: Help with math
|
||||
calculations\n\nYou ONLY have access to the following tools, and should NEVER
|
||||
make up tools that are not listed here:\n\nTool Name: calculate_sum\nTool Arguments:
|
||||
{''a'': {''description'': None, ''type'': ''int''}, ''b'': {''description'':
|
||||
None, ''type'': ''int''}}\nTool Description: Add two numbers together.\n\nIMPORTANT:
|
||||
Use the following format in your response:\n\n```\nThought: you should always
|
||||
think about what to do\nAction: the action to take, only one name of [calculate_sum],
|
||||
just the name, exactly as it''s written.\nAction Input: the input to the action,
|
||||
just a simple JSON object, enclosed in curly braces, using \" to wrap keys and
|
||||
values.\nObservation: the result of the action\n```\n\nOnce all necessary information
|
||||
is gathered, return the following format:\n\n```\nThought: I now know the final
|
||||
answer\nFinal Answer: the final answer to the original input question\n```"},{"role":"user","content":"What
|
||||
is 5 + 3? Use the calculate_sum tool."}],"model":"gpt-4.1-mini"}'
|
||||
headers:
|
||||
User-Agent:
|
||||
- X-USER-AGENT-XXX
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- ACCEPT-ENCODING-XXX
|
||||
authorization:
|
||||
- AUTHORIZATION-XXX
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '1119'
|
||||
content-type:
|
||||
- application/json
|
||||
host:
|
||||
- api.openai.com
|
||||
x-stainless-arch:
|
||||
- X-STAINLESS-ARCH-XXX
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- X-STAINLESS-OS-XXX
|
||||
x-stainless-package-version:
|
||||
- 1.83.0
|
||||
x-stainless-read-timeout:
|
||||
- X-STAINLESS-READ-TIMEOUT-XXX
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.13.3
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
H4sIAAAAAAAAAwAAAP//jFPLbtswELzrKxY8W0Zsy3ajWxv0EbSXFm0CtAokmlxLTCiSIFdFWsP/
|
||||
XlB+SO4D6IUQdnZGs7PkLgFgSrIcmGg4idbp9EY9hbvZsrn7cP/l0/uv77JXzx9fv31UN2/u3U82
|
||||
iQy7eURBJ9ZU2NZpJGXNARYeOWFUna1X2frFfLVa90BrJepIqx2l2XSWtsqodH41X6ZXWTrLjvTG
|
||||
KoGB5fAtAQDY9Wc0aiQ+sxyuJqdKiyHwGll+bgJg3upYYTwEFYgbYpMBFNYQmt57VVWF+dzYrm4o
|
||||
h1sIje20hC4gUIMguBad5oRl6FogazWQBS4lLIEbCYtpYV6KOHV+2Xsqw61xHeWwKxgvWA7LCRRs
|
||||
E78W+8JUVTX25XHbBR7DMZ3WI4AbY4lHvT6RhyOyP2egbe283YTfqGyrjApN6ZEHa+K8gaxjPbpP
|
||||
AB76rLuL+JjztnVUkn3C/nfzRXbQY8OOBzQ7LoKRJa5HrPWJdaFXSiSudBhtiwkuGpQDdVgt76Sy
|
||||
IyAZTf2nm79pHyZXpv4f+QEQAh2hLJ1HqcTlxEObx/gE/tV2Trk3zAL670pgSQp93ITELe/04V6y
|
||||
8CMQtuVWmRq98+pwObeuvF6vVrjMrjdzluyTXwAAAP//AwAHWkpkqwMAAA==
|
||||
headers:
|
||||
CF-RAY:
|
||||
- CF-RAY-XXX
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 03 Dec 2025 17:24:28 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- SET-COOKIE-XXX
|
||||
Strict-Transport-Security:
|
||||
- STS-XXX
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- X-CONTENT-TYPE-XXX
|
||||
access-control-expose-headers:
|
||||
- ACCESS-CONTROL-XXX
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
cf-cache-status:
|
||||
- DYNAMIC
|
||||
openai-organization:
|
||||
- OPENAI-ORG-XXX
|
||||
openai-processing-ms:
|
||||
- '681'
|
||||
openai-project:
|
||||
- OPENAI-PROJECT-XXX
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
x-envoy-upstream-service-time:
|
||||
- '871'
|
||||
x-openai-proxy-wasm:
|
||||
- v0.1
|
||||
x-ratelimit-limit-requests:
|
||||
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||
x-ratelimit-limit-tokens:
|
||||
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||
x-ratelimit-remaining-requests:
|
||||
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||
x-ratelimit-remaining-tokens:
|
||||
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||
x-ratelimit-reset-requests:
|
||||
- X-RATELIMIT-RESET-REQUESTS-XXX
|
||||
x-ratelimit-reset-tokens:
|
||||
- X-RATELIMIT-RESET-TOKENS-XXX
|
||||
x-request-id:
|
||||
- X-REQUEST-ID-XXX
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
- request:
|
||||
body: '{"messages":[{"role":"system","content":"You are Calculator Assistant.
|
||||
You are a helpful calculator assistant\nYour personal goal is: Help with math
|
||||
calculations\n\nYou ONLY have access to the following tools, and should NEVER
|
||||
make up tools that are not listed here:\n\nTool Name: calculate_sum\nTool Arguments:
|
||||
{''a'': {''description'': None, ''type'': ''int''}, ''b'': {''description'':
|
||||
None, ''type'': ''int''}}\nTool Description: Add two numbers together.\n\nIMPORTANT:
|
||||
Use the following format in your response:\n\n```\nThought: you should always
|
||||
think about what to do\nAction: the action to take, only one name of [calculate_sum],
|
||||
just the name, exactly as it''s written.\nAction Input: the input to the action,
|
||||
just a simple JSON object, enclosed in curly braces, using \" to wrap keys and
|
||||
values.\nObservation: the result of the action\n```\n\nOnce all necessary information
|
||||
is gathered, return the following format:\n\n```\nThought: I now know the final
|
||||
answer\nFinal Answer: the final answer to the original input question\n```"},{"role":"user","content":"What
|
||||
is 5 + 3? Use the calculate_sum tool."},{"role":"assistant","content":"```\nThought:
|
||||
I should use the calculate_sum tool to add 5 and 3.\nAction: calculate_sum\nAction
|
||||
Input: {\"a\": 5, \"b\": 3}\n```\nObservation: 8"}],"model":"gpt-4.1-mini"}'
|
||||
headers:
|
||||
User-Agent:
|
||||
- X-USER-AGENT-XXX
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- ACCEPT-ENCODING-XXX
|
||||
authorization:
|
||||
- AUTHORIZATION-XXX
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '1298'
|
||||
content-type:
|
||||
- application/json
|
||||
cookie:
|
||||
- COOKIE-XXX
|
||||
host:
|
||||
- api.openai.com
|
||||
x-stainless-arch:
|
||||
- X-STAINLESS-ARCH-XXX
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- X-STAINLESS-OS-XXX
|
||||
x-stainless-package-version:
|
||||
- 1.83.0
|
||||
x-stainless-read-timeout:
|
||||
- X-STAINLESS-READ-TIMEOUT-XXX
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.13.3
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
H4sIAAAAAAAAAwAAAP//jFJNb5wwEL3zKyyfl2ghBAi3KtJKTS9VlX5I3QiMGcBZY7v20DSK9r9X
|
||||
hs1C2lTqxdLMm/c8b2aeA0KoaGhBKO8Z8sHI8EYc3Ff7pX663Y32w82Qffv46XNUR+Yu+7GjG8/Q
|
||||
9QNwfGFdcD0YCSi0mmFugSF41ShLkyyP0zSfgEE3ID2tMxgmF1E4CCXCeBtfhdskjJITvdeCg6MF
|
||||
+R4QQsjz9PpGVQO/aEG2m5fMAM6xDmhxLiKEWi19hjLnhEOmkG4WkGuFoKbeq6raq7tej12PBXlP
|
||||
lH4kB/9gD6QViknClHsEu1e7KXo3RQXJ96qqqrWqhXZ0zFtTo5QrgCmlkfnRTH7uT8jx7EDqzlhd
|
||||
uz+otBVKuL60wJxWvluH2tAJPQaE3E+TGl+Zp8bqwWCJ+gDTd3F+OevRZUMLGuUnEDUyueQvt9Hm
|
||||
Db2yAWRCutWsKWe8h2ahLothYyP0CghWrv/u5i3t2blQ3f/ILwDnYBCa0lhoBH/teCmz4A/4X2Xn
|
||||
KU8NUwf2p+BQogDrN9FAy0Y5XxV1Tw5hKFuhOrDGivm0WlNeZ2kKV8l1HdPgGPwGAAD//wMAJksH
|
||||
jGkDAAA=
|
||||
headers:
|
||||
CF-RAY:
|
||||
- CF-RAY-XXX
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 03 Dec 2025 17:24:29 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Strict-Transport-Security:
|
||||
- STS-XXX
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- X-CONTENT-TYPE-XXX
|
||||
access-control-expose-headers:
|
||||
- ACCESS-CONTROL-XXX
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
cf-cache-status:
|
||||
- DYNAMIC
|
||||
openai-organization:
|
||||
- OPENAI-ORG-XXX
|
||||
openai-processing-ms:
|
||||
- '427'
|
||||
openai-project:
|
||||
- OPENAI-PROJECT-XXX
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
x-envoy-upstream-service-time:
|
||||
- '442'
|
||||
x-openai-proxy-wasm:
|
||||
- v0.1
|
||||
x-ratelimit-limit-requests:
|
||||
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||
x-ratelimit-limit-tokens:
|
||||
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||
x-ratelimit-remaining-requests:
|
||||
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||
x-ratelimit-remaining-tokens:
|
||||
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||
x-ratelimit-reset-requests:
|
||||
- X-RATELIMIT-RESET-REQUESTS-XXX
|
||||
x-ratelimit-reset-tokens:
|
||||
- X-RATELIMIT-RESET-TOKENS-XXX
|
||||
x-request-id:
|
||||
- X-REQUEST-ID-XXX
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
- request:
|
||||
body: '{"messages":[{"role":"system","content":"You are Calculator Assistant.
|
||||
You are a helpful calculator assistant\nYour personal goal is: Help with math
|
||||
calculations\n\nYou ONLY have access to the following tools, and should NEVER
|
||||
make up tools that are not listed here:\n\nTool Name: calculate_sum\nTool Arguments:
|
||||
{''a'': {''description'': None, ''type'': ''int''}, ''b'': {''description'':
|
||||
None, ''type'': ''int''}}\nTool Description: Add two numbers together.\n\nIMPORTANT:
|
||||
Use the following format in your response:\n\n```\nThought: you should always
|
||||
think about what to do\nAction: the action to take, only one name of [calculate_sum],
|
||||
just the name, exactly as it''s written.\nAction Input: the input to the action,
|
||||
just a simple JSON object, enclosed in curly braces, using \" to wrap keys and
|
||||
values.\nObservation: the result of the action\n```\n\nOnce all necessary information
|
||||
is gathered, return the following format:\n\n```\nThought: I now know the final
|
||||
answer\nFinal Answer: the final answer to the original input question\n```"},{"role":"user","content":"What
|
||||
is 5 + 3? Use the calculate_sum tool."}],"model":"gpt-4.1-mini"}'
|
||||
headers:
|
||||
User-Agent:
|
||||
- X-USER-AGENT-XXX
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- ACCEPT-ENCODING-XXX
|
||||
authorization:
|
||||
- AUTHORIZATION-XXX
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '1119'
|
||||
content-type:
|
||||
- application/json
|
||||
host:
|
||||
- api.openai.com
|
||||
x-stainless-arch:
|
||||
- X-STAINLESS-ARCH-XXX
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- X-STAINLESS-OS-XXX
|
||||
x-stainless-package-version:
|
||||
- 1.83.0
|
||||
x-stainless-read-timeout:
|
||||
- X-STAINLESS-READ-TIMEOUT-XXX
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.13.3
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
H4sIAAAAAAAAAwAAAP//jJLLbtswEEX3+ooB15bhh2zH2iXtJovuYqAPBRJNjiU6FEmQo7ap4X8v
|
||||
JDmW3KZAN1rMmXs1c4enCIApyVJgouIkaqfjD6r+fFf9+KJ2u6/qeO92H1/mrw+ffj25h+OWTVqF
|
||||
3R9R0JtqKmztNJKypsfCIydsXeebdbK5286Wsw7UVqJuZaWjOJnO41oZFS9mi1U8S+J5cpFXVgkM
|
||||
LIVvEQDAqfu2gxqJP1kKnVlXqTEEXiJLr00AzFvdVhgPQQXihthkgMIaQtPNXhRFZp4q25QVpfAI
|
||||
BlECWeBSwgq4kbCEJihTAlUIgmvRaE6Yh6YGslZPM3Mv2q3TW/hWhkfjGkrhlDGesXQ1ydg+Y+ny
|
||||
nJmiKMZDeTw0gbfJmEbrEeDGWOKtWRfH84WcrwFoWzpv9+EPKTsoo0KVe+TBmnbZQNaxjp4jgOcu
|
||||
6OYmO+a8rR3lZF+w+91imfR+bDjwQJebCyRLXI9Um/nkHb9cInGlw+hUTHBRoRykw115I5UdgWi0
|
||||
9d/TvOfdb65M+T/2AxACHaHMnUepxO3GQ5vH9v3/q+2acjcwC+i/K4E5KfTtJSQeeKP7R8nCayCs
|
||||
84MyJXrnVf8yDy7fbtZrXCXb/YJF5+g3AAAA//8DANrSB6yoAwAA
|
||||
headers:
|
||||
CF-RAY:
|
||||
- CF-RAY-XXX
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 03 Dec 2025 19:10:33 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- SET-COOKIE-XXX
|
||||
Strict-Transport-Security:
|
||||
- STS-XXX
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- X-CONTENT-TYPE-XXX
|
||||
access-control-expose-headers:
|
||||
- ACCESS-CONTROL-XXX
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
cf-cache-status:
|
||||
- DYNAMIC
|
||||
openai-organization:
|
||||
- OPENAI-ORG-XXX
|
||||
openai-processing-ms:
|
||||
- '2329'
|
||||
openai-project:
|
||||
- OPENAI-PROJECT-XXX
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
x-envoy-upstream-service-time:
|
||||
- '2349'
|
||||
x-openai-proxy-wasm:
|
||||
- v0.1
|
||||
x-ratelimit-limit-requests:
|
||||
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||
x-ratelimit-limit-tokens:
|
||||
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||
x-ratelimit-remaining-requests:
|
||||
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||
x-ratelimit-remaining-tokens:
|
||||
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||
x-ratelimit-reset-requests:
|
||||
- X-RATELIMIT-RESET-REQUESTS-XXX
|
||||
x-ratelimit-reset-tokens:
|
||||
- X-RATELIMIT-RESET-TOKENS-XXX
|
||||
x-request-id:
|
||||
- X-REQUEST-ID-XXX
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
- request:
|
||||
body: '{"messages":[{"role":"system","content":"You are Calculator Assistant.
|
||||
You are a helpful calculator assistant\nYour personal goal is: Help with math
|
||||
calculations\n\nYou ONLY have access to the following tools, and should NEVER
|
||||
make up tools that are not listed here:\n\nTool Name: calculate_sum\nTool Arguments:
|
||||
{''a'': {''description'': None, ''type'': ''int''}, ''b'': {''description'':
|
||||
None, ''type'': ''int''}}\nTool Description: Add two numbers together.\n\nIMPORTANT:
|
||||
Use the following format in your response:\n\n```\nThought: you should always
|
||||
think about what to do\nAction: the action to take, only one name of [calculate_sum],
|
||||
just the name, exactly as it''s written.\nAction Input: the input to the action,
|
||||
just a simple JSON object, enclosed in curly braces, using \" to wrap keys and
|
||||
values.\nObservation: the result of the action\n```\n\nOnce all necessary information
|
||||
is gathered, return the following format:\n\n```\nThought: I now know the final
|
||||
answer\nFinal Answer: the final answer to the original input question\n```"},{"role":"user","content":"What
|
||||
is 5 + 3? Use the calculate_sum tool."},{"role":"assistant","content":"```\nThought:
|
||||
I need to add 5 and 3 using the calculate_sum tool.\nAction: calculate_sum\nAction
|
||||
Input: {\"a\":5,\"b\":3}\n```\nObservation: 8"}],"model":"gpt-4.1-mini"}'
|
||||
headers:
|
||||
User-Agent:
|
||||
- X-USER-AGENT-XXX
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- ACCEPT-ENCODING-XXX
|
||||
authorization:
|
||||
- AUTHORIZATION-XXX
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '1295'
|
||||
content-type:
|
||||
- application/json
|
||||
cookie:
|
||||
- COOKIE-XXX
|
||||
host:
|
||||
- api.openai.com
|
||||
x-stainless-arch:
|
||||
- X-STAINLESS-ARCH-XXX
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- X-STAINLESS-OS-XXX
|
||||
x-stainless-package-version:
|
||||
- 1.83.0
|
||||
x-stainless-read-timeout:
|
||||
- X-STAINLESS-READ-TIMEOUT-XXX
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.13.3
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
H4sIAAAAAAAAAwAAAP//jJJNb9QwEIbv+RWWz5tqN81+5QaVQHDi0AOIrRKvPUncOmNjTyhQ7X9H
|
||||
zn4kS0HiYsl+5h3POzMvCWNcK14wLltBsnMmvdPd57f+yzKovc/cR/z23ohfn+69yJX1fBYVdv8I
|
||||
ks6qG2k7Z4C0xSOWHgRBzLpYr/L1Zju/vR1AZxWYKGscpfnNIu006jSbZ8t0nqeL/CRvrZYQeMG+
|
||||
Jowx9jKcsVBU8IMXbD47v3QQgmiAF5cgxri3Jr5wEYIOJJD4bITSIgEOtVdVtcP71vZNSwX7wNA+
|
||||
s6d4UAus1igMExiewe/w3XB7M9wKttlhVVXTrB7qPohoDXtjJkAgWhKxNYOfhxM5XBwY2zhv9+EP
|
||||
Ka816tCWHkSwGKsNZB0f6CFh7GHoVH9lnjtvO0cl2ScYvss2p07xcUIjXWxOkCwJM1Ftz+AqX6mA
|
||||
hDZh0msuhWxBjdJxMKJX2k5AMnH9upq/5T4619j8T/oRSAmOQJXOg9Ly2vEY5iEu8L/CLl0eCuYB
|
||||
/HctoSQNPk5CQS16c9wqHn4Ggq6sNTbgndfH1apduV2vVrDMt/uMJ4fkNwAAAP//AwCRC7shaQMA
|
||||
AA==
|
||||
headers:
|
||||
CF-RAY:
|
||||
- CF-RAY-XXX
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 03 Dec 2025 19:10:35 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Strict-Transport-Security:
|
||||
- STS-XXX
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- X-CONTENT-TYPE-XXX
|
||||
access-control-expose-headers:
|
||||
- ACCESS-CONTROL-XXX
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
cf-cache-status:
|
||||
- DYNAMIC
|
||||
openai-organization:
|
||||
- OPENAI-ORG-XXX
|
||||
openai-processing-ms:
|
||||
- '1647'
|
||||
openai-project:
|
||||
- OPENAI-PROJECT-XXX
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
x-envoy-upstream-service-time:
|
||||
- '1694'
|
||||
x-openai-proxy-wasm:
|
||||
- v0.1
|
||||
x-ratelimit-limit-requests:
|
||||
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||
x-ratelimit-limit-tokens:
|
||||
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||
x-ratelimit-remaining-requests:
|
||||
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||
x-ratelimit-remaining-tokens:
|
||||
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||
x-ratelimit-reset-requests:
|
||||
- X-RATELIMIT-RESET-REQUESTS-XXX
|
||||
x-ratelimit-reset-tokens:
|
||||
- X-RATELIMIT-RESET-TOKENS-XXX
|
||||
x-request-id:
|
||||
- X-REQUEST-ID-XXX
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -309,3 +309,188 @@ class TestLLMHooksIntegration:
|
||||
clear_all_llm_call_hooks()
|
||||
hooks = get_before_llm_call_hooks()
|
||||
assert len(hooks) == 0
|
||||
|
||||
@pytest.mark.vcr()
|
||||
def test_lite_agent_hooks_integration_with_real_llm(self):
|
||||
"""Test that LiteAgent executes before/after LLM call hooks and prints messages correctly."""
|
||||
import os
|
||||
from crewai.lite_agent import LiteAgent
|
||||
|
||||
# Skip if no API key available
|
||||
if not os.environ.get("OPENAI_API_KEY"):
|
||||
pytest.skip("OPENAI_API_KEY not set - skipping real LLM test")
|
||||
|
||||
# Track hook invocations
|
||||
hook_calls = {"before": [], "after": []}
|
||||
|
||||
def before_llm_call_hook(context: LLMCallHookContext) -> bool:
|
||||
"""Log and verify before hook execution."""
|
||||
print(f"\n[BEFORE HOOK] Agent: {context.agent.role if context.agent else 'None'}")
|
||||
print(f"[BEFORE HOOK] Iterations: {context.iterations}")
|
||||
print(f"[BEFORE HOOK] Message count: {len(context.messages)}")
|
||||
print(f"[BEFORE HOOK] Messages: {context.messages}")
|
||||
|
||||
# Track the call
|
||||
hook_calls["before"].append({
|
||||
"iterations": context.iterations,
|
||||
"message_count": len(context.messages),
|
||||
"has_task": context.task is not None,
|
||||
"has_crew": context.crew is not None,
|
||||
})
|
||||
|
||||
return True # Allow execution
|
||||
|
||||
def after_llm_call_hook(context: LLMCallHookContext) -> str | None:
|
||||
"""Log and verify after hook execution."""
|
||||
print(f"\n[AFTER HOOK] Agent: {context.agent.role if context.agent else 'None'}")
|
||||
print(f"[AFTER HOOK] Iterations: {context.iterations}")
|
||||
print(f"[AFTER HOOK] Response: {context.response[:100] if context.response else 'None'}...")
|
||||
print(f"[AFTER HOOK] Final message count: {len(context.messages)}")
|
||||
|
||||
# Track the call
|
||||
hook_calls["after"].append({
|
||||
"iterations": context.iterations,
|
||||
"has_response": context.response is not None,
|
||||
"response_length": len(context.response) if context.response else 0,
|
||||
})
|
||||
|
||||
# Optionally modify response
|
||||
if context.response:
|
||||
return f"[HOOKED] {context.response}"
|
||||
return None
|
||||
|
||||
# Register hooks
|
||||
register_before_llm_call_hook(before_llm_call_hook)
|
||||
register_after_llm_call_hook(after_llm_call_hook)
|
||||
|
||||
try:
|
||||
# Create LiteAgent
|
||||
lite_agent = LiteAgent(
|
||||
role="Test Assistant",
|
||||
goal="Answer questions briefly",
|
||||
backstory="You are a helpful test assistant",
|
||||
verbose=True,
|
||||
)
|
||||
|
||||
# Verify hooks are loaded
|
||||
assert len(lite_agent.before_llm_call_hooks) > 0, "Before hooks not loaded"
|
||||
assert len(lite_agent.after_llm_call_hooks) > 0, "After hooks not loaded"
|
||||
|
||||
# Execute with a simple prompt
|
||||
result = lite_agent.kickoff("Say 'Hello World' and nothing else")
|
||||
|
||||
|
||||
# Verify hooks were called
|
||||
assert len(hook_calls["before"]) > 0, "Before hook was never called"
|
||||
assert len(hook_calls["after"]) > 0, "After hook was never called"
|
||||
|
||||
# Verify context had correct attributes for LiteAgent (used in flows)
|
||||
# LiteAgent doesn't have task/crew context, unlike agents in CrewBase
|
||||
before_call = hook_calls["before"][0]
|
||||
assert before_call["has_task"] is False, "Task should be None for LiteAgent in flows"
|
||||
assert before_call["has_crew"] is False, "Crew should be None for LiteAgent in flows"
|
||||
assert before_call["message_count"] > 0, "Should have messages"
|
||||
|
||||
# Verify after hook received response
|
||||
after_call = hook_calls["after"][0]
|
||||
assert after_call["has_response"] is True, "After hook should have response"
|
||||
assert after_call["response_length"] > 0, "Response should not be empty"
|
||||
|
||||
# Verify response was modified by after hook
|
||||
# Note: The hook modifies the raw LLM response, but LiteAgent then parses it
|
||||
# to extract the "Final Answer" portion. We check the messages to see the modification.
|
||||
assert len(result.messages) > 2, "Should have assistant message in messages"
|
||||
last_message = result.messages[-1]
|
||||
assert last_message["role"] == "assistant", "Last message should be from assistant"
|
||||
assert "[HOOKED]" in last_message["content"], "Hook should have modified the assistant message"
|
||||
|
||||
|
||||
finally:
|
||||
# Clean up hooks
|
||||
unregister_before_llm_call_hook(before_llm_call_hook)
|
||||
unregister_after_llm_call_hook(after_llm_call_hook)
|
||||
|
||||
@pytest.mark.vcr()
|
||||
def test_direct_llm_call_hooks_integration(self):
|
||||
"""Test that hooks work for direct llm.call() without agents."""
|
||||
import os
|
||||
from crewai.llm import LLM
|
||||
|
||||
# Skip if no API key available
|
||||
if not os.environ.get("OPENAI_API_KEY"):
|
||||
pytest.skip("OPENAI_API_KEY not set - skipping real LLM test")
|
||||
|
||||
# Track hook invocations
|
||||
hook_calls = {"before": [], "after": []}
|
||||
|
||||
def before_hook(context: LLMCallHookContext) -> bool:
|
||||
"""Log and verify before hook execution."""
|
||||
print(f"\n[BEFORE HOOK] Agent: {context.agent}")
|
||||
print(f"[BEFORE HOOK] Task: {context.task}")
|
||||
print(f"[BEFORE HOOK] Crew: {context.crew}")
|
||||
print(f"[BEFORE HOOK] LLM: {context.llm}")
|
||||
print(f"[BEFORE HOOK] Iterations: {context.iterations}")
|
||||
print(f"[BEFORE HOOK] Message count: {len(context.messages)}")
|
||||
|
||||
# Track the call
|
||||
hook_calls["before"].append({
|
||||
"agent": context.agent,
|
||||
"task": context.task,
|
||||
"crew": context.crew,
|
||||
"llm": context.llm is not None,
|
||||
"message_count": len(context.messages),
|
||||
})
|
||||
|
||||
return True # Allow execution
|
||||
|
||||
def after_hook(context: LLMCallHookContext) -> str | None:
|
||||
"""Log and verify after hook execution."""
|
||||
print(f"\n[AFTER HOOK] Agent: {context.agent}")
|
||||
print(f"[AFTER HOOK] Response: {context.response[:100] if context.response else 'None'}...")
|
||||
|
||||
# Track the call
|
||||
hook_calls["after"].append({
|
||||
"has_response": context.response is not None,
|
||||
"response_length": len(context.response) if context.response else 0,
|
||||
})
|
||||
|
||||
# Modify response
|
||||
if context.response:
|
||||
return f"[HOOKED] {context.response}"
|
||||
return None
|
||||
|
||||
# Register hooks
|
||||
register_before_llm_call_hook(before_hook)
|
||||
register_after_llm_call_hook(after_hook)
|
||||
|
||||
try:
|
||||
# Create LLM and make direct call
|
||||
llm = LLM(model="gpt-4o-mini")
|
||||
result = llm.call([{"role": "user", "content": "Say hello"}])
|
||||
|
||||
print(f"\n[TEST] Final result: {result}")
|
||||
|
||||
# Verify hooks were called
|
||||
assert len(hook_calls["before"]) > 0, "Before hook was never called"
|
||||
assert len(hook_calls["after"]) > 0, "After hook was never called"
|
||||
|
||||
# Verify context had correct attributes for direct LLM calls
|
||||
before_call = hook_calls["before"][0]
|
||||
assert before_call["agent"] is None, "Agent should be None for direct LLM calls"
|
||||
assert before_call["task"] is None, "Task should be None for direct LLM calls"
|
||||
assert before_call["crew"] is None, "Crew should be None for direct LLM calls"
|
||||
assert before_call["llm"] is True, "LLM should be present"
|
||||
assert before_call["message_count"] > 0, "Should have messages"
|
||||
|
||||
# Verify after hook received response
|
||||
after_call = hook_calls["after"][0]
|
||||
assert after_call["has_response"] is True, "After hook should have response"
|
||||
assert after_call["response_length"] > 0, "Response should not be empty"
|
||||
|
||||
# Verify response was modified by after hook
|
||||
assert "[HOOKED]" in result, "Response should be modified by after hook"
|
||||
|
||||
finally:
|
||||
# Clean up hooks
|
||||
unregister_before_llm_call_hook(before_hook)
|
||||
unregister_after_llm_call_hook(after_hook)
|
||||
|
||||
@@ -496,3 +496,97 @@ class TestToolHooksIntegration:
|
||||
clear_all_tool_call_hooks()
|
||||
hooks = get_before_tool_call_hooks()
|
||||
assert len(hooks) == 0
|
||||
|
||||
@pytest.mark.vcr()
|
||||
def test_lite_agent_hooks_integration_with_real_tool(self):
|
||||
"""Test that LiteAgent executes before/after tool call hooks with real tool calls."""
|
||||
import os
|
||||
from crewai.lite_agent import LiteAgent
|
||||
from crewai.tools import tool
|
||||
|
||||
# Skip if no API key available
|
||||
if not os.environ.get("OPENAI_API_KEY"):
|
||||
pytest.skip("OPENAI_API_KEY not set - skipping real tool test")
|
||||
|
||||
# Track hook invocations
|
||||
hook_calls = {"before": [], "after": []}
|
||||
|
||||
# Create a simple test tool
|
||||
@tool("calculate_sum")
|
||||
def calculate_sum(a: int, b: int) -> int:
|
||||
"""Add two numbers together."""
|
||||
return a + b
|
||||
|
||||
def before_tool_call_hook(context: ToolCallHookContext) -> bool:
|
||||
"""Log and verify before hook execution."""
|
||||
print(f"\n[BEFORE HOOK] Tool: {context.tool_name}")
|
||||
print(f"[BEFORE HOOK] Tool input: {context.tool_input}")
|
||||
print(f"[BEFORE HOOK] Agent: {context.agent.role if context.agent else 'None'}")
|
||||
print(f"[BEFORE HOOK] Task: {context.task}")
|
||||
print(f"[BEFORE HOOK] Crew: {context.crew}")
|
||||
|
||||
# Track the call
|
||||
hook_calls["before"].append({
|
||||
"tool_name": context.tool_name,
|
||||
"tool_input": context.tool_input,
|
||||
"has_agent": context.agent is not None,
|
||||
"has_task": context.task is not None,
|
||||
"has_crew": context.crew is not None,
|
||||
})
|
||||
|
||||
return True # Allow execution
|
||||
|
||||
def after_tool_call_hook(context: ToolCallHookContext) -> str | None:
|
||||
"""Log and verify after hook execution."""
|
||||
print(f"\n[AFTER HOOK] Tool: {context.tool_name}")
|
||||
print(f"[AFTER HOOK] Tool result: {context.tool_result}")
|
||||
print(f"[AFTER HOOK] Agent: {context.agent.role if context.agent else 'None'}")
|
||||
|
||||
# Track the call
|
||||
hook_calls["after"].append({
|
||||
"tool_name": context.tool_name,
|
||||
"tool_result": context.tool_result,
|
||||
"has_result": context.tool_result is not None,
|
||||
})
|
||||
|
||||
return None # Don't modify result
|
||||
|
||||
# Register hooks
|
||||
register_before_tool_call_hook(before_tool_call_hook)
|
||||
register_after_tool_call_hook(after_tool_call_hook)
|
||||
|
||||
try:
|
||||
# Create LiteAgent with the tool
|
||||
lite_agent = LiteAgent(
|
||||
role="Calculator Assistant",
|
||||
goal="Help with math calculations",
|
||||
backstory="You are a helpful calculator assistant",
|
||||
tools=[calculate_sum],
|
||||
verbose=True,
|
||||
)
|
||||
|
||||
# Execute with a prompt that should trigger tool usage
|
||||
result = lite_agent.kickoff("What is 5 + 3? Use the calculate_sum tool.")
|
||||
|
||||
# Verify hooks were called
|
||||
assert len(hook_calls["before"]) > 0, "Before hook was never called"
|
||||
assert len(hook_calls["after"]) > 0, "After hook was never called"
|
||||
|
||||
# Verify context had correct attributes for LiteAgent (used in flows)
|
||||
# LiteAgent doesn't have task/crew context, unlike agents in CrewBase
|
||||
before_call = hook_calls["before"][0]
|
||||
assert before_call["tool_name"] == "calculate_sum", "Tool name should be 'calculate_sum'"
|
||||
assert "a" in before_call["tool_input"], "Tool input should have 'a' parameter"
|
||||
assert "b" in before_call["tool_input"], "Tool input should have 'b' parameter"
|
||||
|
||||
# Verify after hook received result
|
||||
after_call = hook_calls["after"][0]
|
||||
assert after_call["has_result"] is True, "After hook should have tool result"
|
||||
assert after_call["tool_name"] == "calculate_sum", "Tool name should match"
|
||||
# The result should contain the sum (8)
|
||||
assert "8" in str(after_call["tool_result"]), "Tool result should contain the sum"
|
||||
|
||||
finally:
|
||||
# Clean up hooks
|
||||
unregister_before_tool_call_hook(before_tool_call_hook)
|
||||
unregister_after_tool_call_hook(after_tool_call_hook)
|
||||
|
||||
Reference in New Issue
Block a user