Asterisk AGI, python

Hi dear TEAM ,
the problem is that i am sending commands to asterisk from python and python is not waiting the response from asterisk and continues implementing python codes this is initial problem but now i have created a variable which assigns value of steps asterisk, current problem is after one loop asterisk did automaticly hangup
can you give us direction or any other information regarding the issue of ours
Thank you for your help

this is my code.

#!/opt/call_center/venv/bin/python3 
import websockets
from utils import *
from endpoints import *
import time
import sys
from websockets.sync.client import connect
from asterisk.agi import AGI


agi = AGI()



KUTING = {
    'uz': '/tmp/kuting',
    'ru': '/tmp/kuting_ru'
}

GAPIRING = {
    'uz': '/tmp/gapiring',
    'ru': '/tmp/gapiring_ru'
}

JAVOB_TILI = {
    "uz": "o'zbekcha javob ber",
    "ru": "на русском языке"
}

RESPONSE_PLAY_END = 'response_play_end'
RECORD_END = 'record_end'
language = 'uz'



def wait_for(step):
    print(f"VERBOSE \"{step} \ 1")
    current_step = agi.get_variable('STEP')
    while current_step != step:
        current_step = agi.get_variable('STEP')
        print(f"VERBOSE \"{current_step} \ 1")
    return True
        

def websocket_client(caller_number):

    uri = "ws://server_ip/chatbot/

"
    print(f"VERBOSE \"{caller_number} phone\ 1")
    
    with connect(uri) as websocket:
        print(f"VERBOSE \"websocket connected\ 1")
        # agi.answer()
        language = 'uz'
        agi.appexec('Playback', '/tmp/kirish_sozi_bank')
        result = agi.get_result()
        try:
            while True:
                print(f"VERBOSE \" gapiring \ 1")
                agi.appexec('Playback', f'{GAPIRING[language]}')
                
                agi.record_file(f'/tmp/{caller_number}', format='wav', silence=2)
                result = agi.get_result()
                print(f"VERBOSE \"yozib olish yakunlandi")

                agi.set_variable("STEP", RECORD_END)
                wait_for(RECORD_END)
                language = detect_language(caller_number)

                agi.appexec('Playback', f'{KUTING[language]}')
                result = agi.get_result()
                transcript = send_audio_to_stt(caller_number, language)            

                websocket.send(transcript + f"\n {JAVOB_TILI[language]}")
                response =  websocket.recv()
                print(f"VERBOSE \"{response} socketdan \ 1")
                save_log(caller_number, transcript, response)

                send_to_tts(response, language, caller_number)

                agi.appexec('Playback', f'/tmp/response_{caller_number}')
                agi.set_variable("STEP", RESPONSE_PLAY_END)
                wait_for(RESPONSE_PLAY_END)
            
        except Exception as e:
            print(f"VERBOSE \"{e}\ 1", file=sys.stderr)
       

caller_number = sys.argv[1]
websocket_client(caller_number)

Asteriks extensions

[websocketli]
exten => _X,1,NoOp()   
    same => n,AGI(/opt/call_center/agi.py,${CALLERID(num)})

Casual observation looks like you are violating the AGI protocol. Where do you read the response to the VERBOSE request? Does your library provide a .verbose method?

Also, do you need a closing quoted double quote?

asterisk> agi set debug on

may yield clues.

the .verbose method of the library didn’t work
then I used this print(f"VERBOSE \"some text \1")
this prints the text to the asterisk console

But it leaves an outstanding response, from the AGI server, in the input queue, so when you try to do the read, that response is matched up with the read request.

I found this warning in asterisk console

WARNING[14545][C-00000035]: taskprocessor.c:1225 taskprocessor_push: The 'stasis/m:channel:all-00000062' task processor queue reached 500 scheduled tasks again.

And I thought sent a lot of VERBOSE command in wait_for function then commented print function in while loop.

print(f"VERBOSE \"{current_step} \ 1")
def wait_for(step):
    print(f"VERBOSE \"{step} \ 1")
    current_step = agi.get_variable('STEP')
    while current_step != step:
        current_step = agi.get_variable('STEP')
        #print(f"VERBOSE \"{current_step} \ 1")
    return True

The call is no longer automatically hangup.

You need a delay in that loop! Otherwise you are putting a 100% load on at least one CPU core for each call running that AGI.

AGI also feels like the wrong API. If waiting for an event, you should probably be using AMI events. An AGI script can also use AMI.

This kinda implies you’re doing something wrong. Maybe mismatching quotes? What is displayed (with AGI debug enabled) on the console? You should resolve this and only use library methods.

AGI is a very simple ‘plain text’ protocol. When Asterisk creates the process to execute your AGI, Asterisk communicates with that process via the STDIN and STDOUT of the created process. Thus, your AGI (via the library) make a request by ‘printing’ the request on STDOUT. The library method will read the response from STDIN. Many programmers miss the subtlety and add their own ‘debugging’ print statements, thus breaking the protocol.

For every request, there is a response that must be read. Lather, rinse, repeat.

In addition, when the process is created, Asterisk ‘prints’ a bunch of variables to the STDIN of the process. Some libraries automatically read this, some don’t. If your library does not, there is probably a method to read this ‘AGI environment.’ This ‘backlog’ needs to be ‘drained’ before making AGI requests.

AGI is a synchronous protocol. If you’re looking for events, listening for AMI events sounds like a better approach. Note that you can use AMI in an AGI.

I can’t see any way to invoke AMI actions from AGI. You can use EXEC to invoke dialplan applications, but that’s about it.

It does work the other way: you can invoke AGI commands from AMI, provided the channel is put into AsyncAGI state.

The same way as you invoke them from any other script. The same script can be both an AGI server and an AMI client.

Seems to me, if you try to execute AMI operations on the channel that invoked you via AGI, you could deadlock.

How? Via TCP. Is there another way?

Why? Because (back in 2009) there was some piece of data I needed that was not available via exec meetme list but it was via AMI meetme list.

The channel isn’t locked whilst the whole time AGI is running, and there is reference counting to ensure that it can’t be fully removed.

I’m not sure of the current internals, but the original implementation would cause the channel used by AGI to appear to hang up, if was forced to another place in the dialplan.

Seems like a somewhat roundabout way of doing things: Asterisk → AGI → new process → AMI connection → Asterisk.

AMI + AsyncAGI seems more efficient.

Thank you very much to all of you.