Skip to content

Commit a7e4f92

Browse files
authored
optimize error stack of module (#897)
1 parent 1d00fde commit a7e4f92

12 files changed

Lines changed: 145 additions & 71 deletions

File tree

.github/workflows/main.yml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ jobs:
144144
uses: ./.github/actions/run_tests
145145
with:
146146
working_directory: ${{ env.CI_PATH }}
147-
changed_files: ${{ needs.Clone.outputs.changed_files }}
147+
changed_files: ${{ needs.clone.outputs.changed_files }}
148148
markers: "not skip_on_linux"
149149
tests_dir: "tests/basic_tests"
150150
launcher: "empty"
@@ -163,13 +163,15 @@ jobs:
163163
uses: ./.github/actions/run_tests
164164
with:
165165
working_directory: ${{ env.CI_PATH }}
166-
changed_files: ${{ needs.Clone.outputs.changed_files }}
166+
changed_files: ${{ needs.clone.outputs.changed_files }}
167167
markers: "not skip_on_linux"
168168
tests_dir: "tests/advanced_tests"
169169

170170
engine_tests:
171171
runs-on: tps_sco_nv
172172
needs: [ clone ]
173+
if: |
174+
contains(needs.clone.outputs.changed_files, 'lazyllm/engine/')
173175
steps:
174176
- name: Checkout code
175177
uses: actions/checkout@v4
@@ -180,7 +182,7 @@ jobs:
180182
uses: ./.github/actions/run_tests
181183
with:
182184
working_directory: ${{ env.CI_PATH }}
183-
changed_files: ${{ needs.Clone.outputs.changed_files }}
185+
changed_files: ${{ needs.clone.outputs.changed_files }}
184186
markers: "not skip_on_linux"
185187
tests_dir: "tests/engine_tests"
186188

@@ -230,7 +232,7 @@ jobs:
230232
uses: ./.github/actions/run_tests
231233
with:
232234
working_directory: ${{ env.CI_PATH }}
233-
changed_files: ${{ needs.Clone.outputs.changed_files }}
235+
changed_files: ${{ needs.clone.outputs.changed_files }}
234236
markers: "not skip_on_linux"
235237
tests_dir: "tests/charge_tests"
236238

@@ -454,7 +456,7 @@ jobs:
454456
tests/charge_tests
455457
fi
456458
env:
457-
CHANGED_FILES: ${{ needs.Clone.outputs.changed_files }}
459+
CHANGED_FILES: ${{ needs.clone.outputs.changed_files }}
458460
LAZYLLM_KIMI_API_KEY: ${{ secrets.LAZYLLM_KIMI_API_KEY }}
459461
LAZYLLM_GLM_API_KEY: ${{ secrets.LAZYLLM_GLM_API_KEY }}
460462
LAZYLLM_GLM_MODEL_NAME: ${{ secrets.LAZYLLM_GLM_MODEL_NAME }}
@@ -580,7 +582,7 @@ jobs:
580582
--override-ini=cache_dir=tests/charge_tests/.pytest_cache tests/charge_tests
581583
fi
582584
env:
583-
CHANGED_FILES: ${{ needs.Clone.outputs.changed_files }}
585+
CHANGED_FILES: ${{ needs.clone.outputs.changed_files }}
584586
LAZYLLM_KIMI_API_KEY: ${{ secrets.LAZYLLM_KIMI_API_KEY }}
585587
LAZYLLM_GLM_API_KEY: ${{ secrets.LAZYLLM_GLM_API_KEY }}
586588
LAZYLLM_GLM_MODEL_NAME: ${{ secrets.LAZYLLM_GLM_MODEL_NAME }}

lazyllm/common/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from .common import FlatList, Identity, ResultCollector, ArgsDict, CaseInsensitiveDict
44
from .common import ReprRule, make_repr, modify_repr, is_valid_url, is_valid_path, SingletonMeta, SingletonABCMeta
55
from .common import once_flag, call_once, once_wrapper, singleton, reset_on_pickle, Finalizer
6+
from .inspection import _get_callsite
7+
from .exception import _trim_traceback, _register_trim_module, HandledException, _change_exception_type
68
from .text import Color, colored_text
79
from .option import Option, OptionIter
810
from .threading import Thread, ThreadPoolExecutor
@@ -22,6 +24,9 @@
2224
'_get_base_cls_from_registry',
2325
'Register',
2426

27+
# inspection
28+
'_get_callsite',
29+
2530
# utils
2631
'FlatList',
2732
'ReadOnlyWrapper',
@@ -50,6 +55,12 @@
5055
'Finalizer',
5156
'redis_client',
5257

58+
# exception
59+
'_trim_traceback',
60+
'_register_trim_module',
61+
'HandledException',
62+
'_change_exception_type',
63+
5364
# arg praser
5465
'LazyLLMCMD',
5566
'package',

lazyllm/common/exception.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import sys
2+
import types
3+
4+
5+
_expected_trip_modules = {}
6+
_expected_trip_continuous_modules = {}
7+
8+
def _register_trim_module(expected: dict, continuous: bool = False):
9+
expected = {k: [v] if isinstance(v, str) else v for k, v in expected.items()}
10+
if continuous:
11+
global _expected_trip_continuous_modules
12+
_expected_trip_continuous_modules.update(expected)
13+
else:
14+
global _expected_trip_modules
15+
_expected_trip_modules.update(expected)
16+
17+
def _is_lazyllm_internal_frame(frame, continuous: bool = False):
18+
global _expected_trip_modules, _expected_trip_continuous_modules
19+
expected = _expected_trip_continuous_modules if continuous else _expected_trip_modules
20+
mod = frame.f_globals.get('__name__', '')
21+
if not mod.startswith('lazyllm.') and 'lazyllm/components/deploy/relay' not in frame.f_code.co_filename: return False
22+
if mod in expected and frame.f_code.co_name in expected[mod]: return True
23+
return False
24+
25+
def _trim_traceback(tb):
26+
kept, keep_call = [], True
27+
global _expected_trip_modules, _expected_trip_continuous_modules
28+
while tb:
29+
if not _is_lazyllm_internal_frame(tb.tb_frame):
30+
if _is_lazyllm_internal_frame(tb.tb_frame, continuous=True):
31+
if keep_call:
32+
kept.append(tb)
33+
keep_call = False
34+
else:
35+
kept.append(tb)
36+
keep_call = True
37+
tb = tb.tb_next
38+
39+
new_tb = None
40+
for tb in reversed(kept):
41+
new_tb = types.TracebackType(tb_next=new_tb, tb_frame=tb.tb_frame, tb_lasti=tb.tb_lasti, tb_lineno=tb.tb_lineno)
42+
return new_tb
43+
44+
_original_excepthook = sys.excepthook
45+
46+
def _lazyllm_excepthook(exc_type, exc_value, tb):
47+
_original_excepthook(exc_type, exc_value, _trim_traceback(tb))
48+
49+
sys.excepthook = _lazyllm_excepthook
50+
51+
52+
class HandledException(Exception):
53+
''' Builtin class. Exception that is intended to be handled exceptions. '''
54+
pass
55+
56+
def _change_exception_type(e, new_type):
57+
new_exc = new_type(str(e)).with_traceback(e.__traceback__)
58+
new_exc.__cause__ = e.__cause__
59+
new_exc.__context__ = e.__context__
60+
new_exc.__suppress_context__ = e.__suppress_context__
61+
return new_exc

lazyllm/common/inspection.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import os
2+
import inspect
3+
4+
def _get_callsite(depth: int = 1):
5+
try:
6+
frame = inspect.currentframe()
7+
for _ in range(depth): frame = frame.f_back
8+
if frame is None: return None
9+
else:
10+
while frame.f_code.co_name == '__setattr__' and frame.f_globals.get('__name__', '') == 'lazyllm.common.bind':
11+
frame = frame.f_back
12+
return f'"file: {os.path.abspath(frame.f_code.co_filename)}", line {frame.f_lineno}'
13+
except Exception:
14+
return None

lazyllm/components/deploy/relay/base.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class RelayServer(LazyLLMDeployBase):
1818

1919
def __init__(self, port=None, *, func=None, pre_func=None, post_func=None, pythonpath=None,
2020
log_path=None, cls=None, launcher=launchers.remote(sync=False), num_replicas: int = 1, # noqa B008
21-
security_key: Optional[str] = None):
21+
security_key: Optional[str] = None, defined_pos: Optional[str] = None):
2222
# func must dump in __call__ to wait for dependancies.
2323
self._func = func
2424
self._pre = dump_obj(pre_func)
@@ -27,6 +27,7 @@ def __init__(self, port=None, *, func=None, pre_func=None, post_func=None, pytho
2727
self._pythonpath = pythonpath
2828
self._num_replicas = num_replicas
2929
self._security_key = security_key
30+
self._defined_pos = defined_pos
3031
super().__init__(launcher=launcher)
3132
self.temp_folder = make_log_dir(log_path, cls or 'relay') if log_path else None
3233

@@ -49,12 +50,14 @@ def impl():
4950
cmd += f'--num_replicas={self._num_replicas}'
5051
if self._security_key:
5152
cmd += f'--security_key="{self._security_key}" '
53+
if self._defined_pos:
54+
cmd += '--defined_pos="{}" '.format(self._defined_pos.replace('"', r'\"'))
5255
if self.temp_folder: cmd += f' 2>&1 | tee {get_log_path(self.temp_folder)}'
5356
return cmd
5457

5558
return LazyLLMCMD(cmd=impl, return_value=self.geturl,
5659
checkf=verify_ray_func if config['use_ray'] else verify_fastapi_func,
57-
no_displays=['function', 'before_function', 'after_function', 'security_key'])
60+
no_displays=['function', 'before_function', 'after_function', 'security_key', 'defined_pos'])
5861

5962
def geturl(self, job=None):
6063
if job is None:

lazyllm/components/deploy/relay/server.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import lazyllm
1010
from lazyllm import kwargs, package, load_obj
1111
from lazyllm import FastapiApp, globals
12+
from lazyllm.common import _trim_traceback, _register_trim_module
1213
import time
1314
import pickle
1415
import codecs
@@ -38,6 +39,7 @@
3839
parser.add_argument('--pythonpath')
3940
parser.add_argument('--num_replicas', type=int, default=1, help='num of ray replicas')
4041
parser.add_argument('--security_key', type=str, default=None, help='security key')
42+
parser.add_argument('--defined_pos', type=str, default=None, help='user defined positional')
4143
args = parser.parse_args()
4244

4345
if args.pythonpath:
@@ -50,6 +52,12 @@
5052
after_func = load_obj(args.after_function)
5153

5254

55+
_register_trim_module({'__main__': ['async_wrapper', 'impl']})
56+
_err_msg = ('service of ServerModule execuate failed.\n\nThe above exception was the direct cause '
57+
'of the following exception in service of ServerModule')
58+
_err_msg += (f' defined at `{args.defined_pos}`' if args.defined_pos else '') + ':\n'
59+
60+
5361
app = FastAPI()
5462
FastapiApp.update()
5563

@@ -82,8 +90,10 @@ async def lazyllm_call(request: Request):
8290
return Response(content=codecs.encode(pickle.dumps(r), 'base64'))
8391
except requests.RequestException as e:
8492
return Response(content=f'{str(e)}', status_code=500)
85-
except Exception as e:
86-
return Response(content=f'{e}\n--- traceback ---\n{traceback.format_exc()}', status_code=500)
93+
except Exception:
94+
exc_type, exc_value, exc_tb = sys.exc_info()
95+
formatted = ''.join(traceback.format_exception(exc_type, exc_value, _trim_traceback(exc_tb)))
96+
return Response(content=f'{_err_msg}\n{formatted}', status_code=500)
8797

8898
@app.post('/generate')
8999
@security_check
@@ -141,8 +151,10 @@ def generate_stream():
141151
return Response(content=impl(output))
142152
except requests.RequestException as e:
143153
return Response(content=f'{str(e)}', status_code=500)
144-
except Exception as e:
145-
return Response(content=f'{str(e)}\n--- traceback ---\n{traceback.format_exc()}', status_code=500)
154+
except Exception:
155+
exc_type, exc_value, exc_tb = sys.exc_info()
156+
formatted = ''.join(traceback.format_exception(exc_type, exc_value, _trim_traceback(exc_tb)))
157+
return Response(content=f'{_err_msg}\n{formatted}', status_code=500)
146158
finally:
147159
globals.clear()
148160

lazyllm/flow/flow.py

Lines changed: 8 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
import builtins
33
from lazyllm import config
44
from lazyllm.common import LazyLLMRegisterMetaClass, package, kwargs, arguments, bind, root
5-
from lazyllm.common import ReadOnlyWrapper, LOG, globals, locals
5+
from lazyllm.common import ReadOnlyWrapper, LOG, globals, locals, _get_callsite
6+
from lazyllm.common import _register_trim_module, HandledException, _change_exception_type
67
from lazyllm.common.bind import _MetaBind
78
from functools import partial
89
from contextlib import contextmanager
@@ -21,44 +22,10 @@
2122
from itertools import repeat
2223

2324

24-
class FlowException(Exception):
25+
class FlowException(HandledException):
2526
pass
2627

2728

28-
def _is_lazyllm_internal_frame(frame, expected: Optional[dict] = None):
29-
expected = expected or {'lazyllm.flow.flow': ['_run', 'invoke'], 'lazyllm.common.bind': ['__call__']}
30-
mod = frame.f_globals.get('__name__', '')
31-
if not mod.startswith('lazyllm.'): return False
32-
if mod in expected and frame.f_code.co_name in expected[mod]: return True
33-
return False
34-
35-
_is_lazyllm_call_frame = bind(_is_lazyllm_internal_frame, expected={'lazyllm.flow.flow': '__call__'})
36-
37-
def _trim_traceback(tb):
38-
kept, keep_call = [], True
39-
while tb:
40-
if not _is_lazyllm_internal_frame(tb.tb_frame):
41-
if _is_lazyllm_call_frame(tb.tb_frame):
42-
if keep_call:
43-
kept.append(tb)
44-
keep_call = False
45-
else:
46-
kept.append(tb)
47-
keep_call = True
48-
tb = tb.tb_next
49-
50-
new_tb = None
51-
for tb in reversed(kept):
52-
new_tb = types.TracebackType(tb_next=new_tb, tb_frame=tb.tb_frame, tb_lasti=tb.tb_lasti, tb_lineno=tb.tb_lineno)
53-
return new_tb
54-
55-
_original_excepthook = sys.excepthook
56-
57-
def _lazyllm_excepthook(exc_type, exc_value, tb):
58-
_original_excepthook(exc_type, exc_value, _trim_traceback(tb))
59-
60-
sys.excepthook = _lazyllm_excepthook
61-
6229
class _FuncWrap(object):
6330
def __init__(self, f):
6431
self._f = f._f if isinstance(f, _FuncWrap) else f
@@ -76,19 +43,6 @@ def __getattr__(self, __key):
7643
return getattr(self._f, __key)
7744
return super(__class__, self).__getattr__(__key)
7845

79-
80-
def _get_callsite(depth=1):
81-
try:
82-
frame = inspect.currentframe()
83-
for _ in range(depth): frame = frame.f_back
84-
if frame is None: return None
85-
else:
86-
while frame.f_code.co_name == '__setattr__' and frame.f_globals.get('__name__', '') == 'lazyllm.common.bind':
87-
frame = frame.f_back
88-
return f'"file: {os.path.abspath(frame.f_code.co_filename)}", line {frame.f_lineno}'
89-
except Exception:
90-
return None
91-
9246
_oldins = isinstance
9347
def new_ins(obj, cls):
9448
if _oldins(obj, _FuncWrap) and os.getenv('LAZYLLM_ON_CLOUDPICKLE', None) != 'ON':
@@ -105,6 +59,9 @@ def _is_function(f):
10559
_flow_stack = threading.local()
10660
_flow_stack.value = []
10761

62+
_register_trim_module({'lazyllm.flow.flow': ['__call__']}, continuous=True)
63+
_register_trim_module({'lazyllm.flow.flow': ['_run', 'invoke'], 'lazyllm.common.bind': ['__call__']})
64+
10865
class FlowBase(metaclass=_MetaBind):
10966
def __init__(self, *items, item_names=None, auto_capture=False) -> None:
11067
self._father = None
@@ -315,8 +272,7 @@ def invoke(self, it, __input, *, bind_args_source=None, **kw):
315272
return it(*__input, **kw) if isinstance(__input, package) else it(**__input, **kw)
316273
else:
317274
return it(__input, **kw)
318-
except FlowException as e:
319-
raise e
275+
except HandledException as e: raise e
320276
except Exception as e:
321277
try:
322278
pos = self._item_pos[self._items.index(it)]
@@ -329,7 +285,7 @@ def invoke(self, it, __input, *, bind_args_source=None, **kw):
329285
LOG.error(err_msg)
330286
LOG.debug(f'Error type: {type(e).__name__}, Error message: {str(e)}\n'
331287
f'Traceback: {"".join(traceback.format_exception(*sys.exc_info()))}')
332-
raise FlowException(err_msg) from e
288+
raise _change_exception_type(e, FlowException) from None
333289

334290
def bind(self, *args, **kw):
335291
return bind(self, *args, **kw)

lazyllm/module/llms/onlinemodule/supplier/qwen.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,8 @@ def _forward(self, input: str = None, negative_prompt: str = None, n: int = 1, p
406406
if self._api_key: call_params['api_key'] = self._api_key
407407
if seed: call_params['seed'] = seed
408408
task_response = dashscope.ImageSynthesis.async_call(**call_params)
409+
if not task_response.output:
410+
raise RuntimeError('Failed to create image synthesis task, may due to insufficient balance on your account.')
409411
response = dashscope.ImageSynthesis.wait(task=task_response.output.task_id, api_key=self._api_key)
410412
if response.status_code == HTTPStatus.OK:
411413
return encode_query_with_filepaths(None, bytes_to_file([requests.get(result.url).content

0 commit comments

Comments
 (0)