"""Read-only synthetic inventory example. --self-test never uses the network.
--live requires the official openai package and OPENAI_API_KEY and incurs API usage.
Protocol logic tested offline; no paid inference was performed for the article.
"""
import argparse
import json
from types import SimpleNamespace as N

TOOL = {'type':'function','name':'lookup_stock','description':'Read stock for one known product SKU.',
        'parameters':{'type':'object','properties':{'sku':{'type':'string'}},'required':['sku'],'additionalProperties':False},'strict':True}
STOCK = {'DEMO-A':12,'DEMO-B':0}

def dispatch(name, arguments):
    if name != 'lookup_stock':
        return {'error':'unknown_function'}
    try:
        data = json.loads(arguments)
    except (ValueError, TypeError):
        return {'error':'invalid_json'}
    if not isinstance(data, dict) or set(data) != {'sku'} or not isinstance(data['sku'], str):
        return {'error':'invalid_arguments'}
    if data['sku'] not in STOCK:
        return {'error':'unknown_sku','sku':data['sku']}
    return {'sku':data['sku'],'units':STOCK[data['sku']]}

def run(create, max_rounds=4):
    history = [{'role':'user','content':'Use lookup_stock to report units for DEMO-A. Never guess stock.'}]
    trace = []
    for _ in range(max_rounds):
        response = create(model='gpt-6.1-sol', reasoning={'effort':'medium'},
                          tools=[TOOL], input=list(history), max_output_tokens=2048)
        if response.status != 'completed':
            raise RuntimeError('Response not completed: '+str(response.status))
        history.extend(response.output)
        calls = [item for item in response.output if item.type == 'function_call']
        if not calls:
            if not response.output_text.strip():
                raise RuntimeError('No final text')
            if not any(t['name'] == 'lookup_stock' and t['result'] == {'sku': 'DEMO-A', 'units': 12} for t in trace):
                raise RuntimeError('Required inventory tool was never called successfully for DEMO-A')
            return response.output_text, trace
        for call in calls:
            result = dispatch(call.name, call.arguments)
            trace.append({'name':call.name,'call_id':call.call_id,'result':result})
            history.append({'type':'function_call_output','call_id':call.call_id,'output':json.dumps(result)})
    raise RuntimeError('Model round limit reached')

def self_test():
    assert dispatch('bad','{}') == {'error':'unknown_function'}
    assert dispatch('lookup_stock','{') == {'error':'invalid_json'}
    assert dispatch('lookup_stock','{"sku":1}') == {'error':'invalid_arguments'}
    assert dispatch('lookup_stock','{"sku":"MISSING"}')['error'] == 'unknown_sku'
    calls=[]
    reasoning=N(type='reasoning', id='reasoning-fixture')
    call=N(type='function_call',name='lookup_stock',arguments='{"sku":"DEMO-A"}',call_id='call-fixture')
    def fake(**kwargs):
        calls.append(kwargs)
        if len(calls)==1:
            return N(status='completed',output=[reasoning,call],output_text='')
        assert reasoning in kwargs['input'] and call in kwargs['input']
        result=kwargs['input'][-1]
        assert result['call_id']=='call-fixture' and json.loads(result['output'])['units']==12
        return N(status='completed',output=[],output_text='DEMO-A has 12 units.')
    text,trace=run(fake)
    assert '12' in text and len(trace)==1
    for creator,expected in [
        (lambda **kw:N(status='incomplete',output=[],output_text=''), 'not completed'),
        (lambda **kw:N(status='completed',output=[call],output_text=''), 'round limit'),
        (lambda **kw:N(status='completed',output=[],output_text='I guessed 12'), 'never called')]:
        try: run(creator)
        except RuntimeError as e: assert expected in str(e)
        else: raise AssertionError('Expected failure')
    print('offline checks passed')

if __name__=='__main__':
    p=argparse.ArgumentParser(description=__doc__)
    modes=p.add_mutually_exclusive_group(required=True)
    modes.add_argument('--self-test',action='store_true')
    modes.add_argument('--live',action='store_true')
    args=p.parse_args()
    if args.self_test:
        self_test()
    else:
        from openai import OpenAI
        client=OpenAI(base_url="https://api.openai.com/v1",timeout=30.0,max_retries=0)
        answer,trace=run(client.responses.create)
        print(json.dumps(trace,indent=2))
        print(answer)
        print('Manually verify the answer against DEMO-A=12; fluent text is not a correctness check.')
