SSL

Hi There,
First of all, thank you for a great plugin.
I have looked at the other questions regarding SSL and none seem to cover my scenario.
I am having an issue with Self Signed SSL Certificates.
I have created them with this command :
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365
I then set the broadcasting.php as follows :
'pusher' => [
            'driver' => 'pusher',
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'app_id' => env('PUSHER_APP_ID'),
            'options' => [
                'cluster' => env('PUSHER_APP_CLUSTER'),
                'encrypted' => true,
                'host' => 'sockets.myDomainHere.com',
                'port' => 6001,
                'scheme' => 'https',
                'curl_options' => [
                    CURLOPT_SSL_VERIFYHOST => 0,
                    CURLOPT_SSL_VERIFYPEER => 0,
                ]
            ],
        ],

I Then Set the websockets.php as follows ( comment removed for size ):
'ssl' => [
        'local_cert' => '/full/path/to/self/signed/cert.cert',
        'local_pk' => '/full/path/to/self/signed/key.key',
        'passphrase' => 'ThePassphraseIEnteredWhenCreatingTheCertificate',
        'verify_peer' => false,
    ],

On my client I have it as follows :
new Echo({
        auth:{ headers: { 'Authorization': 'Bearer ' + user.token } },
        broadcaster: 'pusher',
        key: '123456',
        wsHost: 'sockets.myDomainHere.com',
        wsPort: 6001,
        wssPort: 6001,
        disableStats: true,
        enabledTransports: ['ws', 'wss']
    });

So I have 2 issues :

If I go to the Debug Dashboard I cannot connect to the Server
ERROR :

pusher.min.js:8 WebSocket connection to 'ws://sockets.myDomainHere.com:6001/app/123456?protocol=7&client=js&version=4.3.1&flash=false' failed: Error during WebSocket handshake: net::ERR_CONNECTION_RESET

If I go to my app frontend, I get this :

WebSocket connection to 'wss://sockets.myDomainHere.com:6001/app/123456?protocol=7&client=js&version=4.3.1&flash=false' failed: Error in connection establishment: net::ERR_CERT_AUTHORITY_INVALID

Any feedback will be great, Thanks.

I think you need to enable the wss transport manually in the view for the dashboard to get it to work on wss (https://github.com/beyondcode/laravel-websockets/blob/master/resources/views/dashboard.blade.php#L127)

You will also need to still trust the self-signed certificate in your OS or in your browser (Google your OS + browser + "trust self-signed certificate" for detailed instruction).

Hope the above helps!

i spent 2 days reading every article and documentation, to figure out the problem, and i turned out to be
to just change the port in the broadcasting config file to ssl port so here is my config snippet
            'driver' => 'pusher',
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'app_id' => env('PUSHER_APP_ID'),
            'options' => [
                'cluster' => env('PUSHER_APP_CLUSTER'),
                'host' => 'socket.example.com',
                'port' => 443,
                'scheme' => 'https',
                'curl_options' => [
                    CURLOPT_SSL_VERIFYHOST => 0,
                    CURLOPT_SSL_VERIFYPEER => 0,
                ]
            ],

I managed to get it working, and it is really great.
In my case it was the certificates I was using, I switched over to Let's Encrypt and it all worked great

Holy @thesimpledesigners, you saved my life!
I struggled with this for days and was about to give up.
 'curl_options' => [
    CURLOPT_SSL_VERIFYHOST => 0,
    CURLOPT_SSL_VERIFYPEER => 0,
]

did the trick.
Thank you so much!

I just want to leave this warning here for anyone who might come along later.
I would really advise anyone to NOT disable VERIFYHOST and VERIFYPEER, that's just throwing away all the security HTTPS provides. This only makes sense to do if you trust the network your application is running on, and that you have no good option for trusting the certificate. I recommend taking a look at https://github.com/paragonie/certainty which should allow you to use an up-to-date trust store, and also provide your own certificates to that store (useful when using self-signed certificates).
A better option over self-signed certs would be to make your own CA instead, which allows you to trust the CA instead of having to trust the individual self-signed certs. For that, check out https://github.com/FiloSottile/mkcert which can make the process of managing a CA  much simpler.

@cbezzy Your configuration is OK, it worked

What I do not understand is that let's encrypt creates the cert and key files that change names monthly....How did you include the paths here if the filenames keep changing?
'ssl' => [
'local_cert' => '/full/path/to/self/signed/cert.cert',
'local_pk' => '/full/path/to/self/signed/key.key',
'passphrase' => 'ThePassphraseIEnteredWhenCreatingTheCertificate',
'verify_peer' => false,
],
Thanks,

@FreddyCrugger I'm not sure they change their names, I have been lets encrypt for all most a year on 4 websites and I have never faced that issue.

@FreddyCrugger I'm not sure they change their names, I have been lets encrypt for all most a year on 4 websites and I have never faced that issue.

Thanks for the reply.
Yes this is the kind of files I get:
/home/account/ssl/account_com_a25e6_65c3b_1577536339_599647d72a3bcf7b.crt
Then 30 days later I get this:
/home/account/ssl/account_com_a25e6_65c3b_1577536339_98797647d873bc87.crt
And so on......I am really stuck on this...

@FreddyCrugger letsencrypt certs are symlinked in /etc/letsencrypt/live/[website_domain]/ so that in "live" folder you have always the current cert with the same static filename.

@ordinov there is no letsencrypt folder in etc folder. let's encrypt do change file name in shared hosting.

In my case Laravel Web sockets couldn't read the SSL file because I didn't the user running the websockets i.e. www-data, etc didn't have permission to access the SSL certificates. So either copying the certificate files to a place where www-data, or the user who will be running the websockets, can access, or setting appropriate permissions. [From stack overflow] https://serverfault.com/questions/216477/what-should-be-the-permissions-of-apache-ssl-directory-certificate-and-key.

Hi all. After two days modifying the package's sources to guess why wss requests were in vain, trying different approachs, working in local and on my server, I understood that the problem may or may not be on wss transport by laravel-websockets, or may even be or not be in the lack of console logs by the package's processes taking place on execution time of the websocket server. At that point  I worked out a solution by reverse proxing with nginx.
Simple and elegant, requests to the socket will be sent towards the app server, under https using your vhost's installed cert,with no need to configure any cert to the .env variables for that putpose, as all traffic between the websovket server an dthe user gets proxied locally on your server through a reverse proxy configured at nginx directives for the vhost where your app may be.

Set mix pusher host to yout app's domain.
Leave the inmediate first level path into your website urls to be /app only for those coming from the Echo calls towards the puaher server from client browsers.
Place the following directives into your nginx config for the website vhost.

(This sample expects puaher host port to be 6001)
location /app {
proxy_pass             http://localhost:6001;
proxy_read_timeout     60;
proxy_connect_timeout  60;
proxy_redirect         off;
# Allow the use of websockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;

}
These nginx directives intercept the requests under the /app path, and make your vhost work for them as a proxy for wss requests.
Apache rules are not an option as mod_rewrite can intercept upgrade headers but cannot proxy them to localhoat directly AND ProxyPass directives ignore upgrade headers and don't support ws protocols.
As you may see at this point, Apache is actually unable to afford a valid rulesed definition for this purpose.
I tried proxing to another vhosts, also combining nginx and apache. The only solution preserving encryption of end user incoming & outgoing requests ended up being this one, which inside the server works in plain http.
Is true it exposes that requests inside the server and could be intercepted whem proxied to localhost by your server's inner dataflows.
Is a weak point altough. Having this part exposed as many would say. As I think, if this is a problem, then is the less important problem as if this part of the data flow is compromised it means the server is in itself compromised.
And please get the app to log in prod at will.

This worked for me
broadcasting.php
`
return [
/*
|--------------------------------------------------------------------------
| Default Broadcaster
|--------------------------------------------------------------------------
|
| This option controls the default broadcaster that will be used by the
| framework when an event needs to be broadcast. You may set this to
| any of the connections defined in the "connections" array below.
|
| Supported: "pusher", "redis", "log", "null"
|
*/

'default' => env('BROADCAST_DRIVER', 'null'),

/*
|--------------------------------------------------------------------------
| Broadcast Connections
|--------------------------------------------------------------------------
|
| Here you may define all of the broadcast connections that will be used
| to broadcast events to other systems or over websockets. Samples of
| each available type of connection are provided inside this array.
|
*/

'connections' => [

    'pusher' => [
        'driver' => 'pusher',
        'key' => env('PUSHER_APP_KEY'),
        'secret' => env('PUSHER_APP_SECRET'),
        'app_id' => env('PUSHER_APP_ID'),
        'options' => [
            'cluster' => env('PUSHER_APP_CLUSTER'),
            'encrypted' => true,
            'host' => '<IP_ADDRESS>',
            'port' => 8443,
            'scheme' => 'https',
            'curl_options' => [
                CURLOPT_SSL_VERIFYHOST => 0,
                CURLOPT_SSL_VERIFYPEER => 0,
            ]
        ],
    ],

    'redis' => [
        'driver' => 'redis',
        'connection' => 'default',
    ],

    'log' => [
        'driver' => 'log',
    ],

    'null' => [
        'driver' => 'null',
    ],

],

];
`
websockets.php
`
return [
/*
 * Set a custom dashboard configuration
 */
'dashboard' => [
    'port' => env('LARAVEL_WEBSOCKETS_PORT', 8443),
],

/*
 * This package comes with multi tenancy out of the box. Here you can
 * configure the different apps that can use the webSockets server.
 *
 * Optionally you specify capacity so you can limit the maximum
 * concurrent connections for a specific app.
 *
 * Optionally you can disable client events so clients cannot send
 * messages to each other via the webSockets.
 */
'apps' => [
    [
        'id' => env('PUSHER_APP_ID'),
        'name' => env('APP_NAME'),
        'key' => env('PUSHER_APP_KEY'),
        'secret' => env('PUSHER_APP_SECRET'),
        'path' => env('PUSHER_APP_PATH'),
        'capacity' => null,
        'enable_client_messages' => true,
        'enable_statistics' => true,
        'encrypted' => true,
    ],
],

/*
 * This class is responsible for finding the apps. The default provider
 * will use the apps defined in this config file.
 *
 * You can create a custom provider by implementing the
 * `AppProvider` interface.
 */
'app_provider' => BeyondCode\LaravelWebSockets\Apps\ConfigAppProvider::class,

/*
 * This array contains the hosts of which you want to allow incoming requests.
 * Leave this empty if you want to accept requests from all hosts.
 */
'allowed_origins' => [
    //
],

/*
 * The maximum request size in kilobytes that is allowed for an incoming WebSocket request.
 */
'max_request_size_in_kb' => 250,

/*
 * This path will be used to register the necessary routes for the package.
 */
'path' => 'laravel-websockets',

/*
 * Dashboard Routes Middleware
 *
 * These middleware will be assigned to every dashboard route, giving you
 * the chance to add your own middleware to this list or change any of
 * the existing middleware. Or, you can simply stick with this list.
 */
'middleware' => [
    'web',
    Authorize::class,
],

'statistics' => [
    /*
     * This model will be used to store the statistics of the WebSocketsServer.
     * The only requirement is that the model should extend
     * `WebSocketsStatisticsEntry` provided by this package.
     */
    'model' => \BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry::class,

    /**
     * The Statistics Logger will, by default, handle the incoming statistics, store them
     * and then release them into the database on each interval defined below.
     */
    'logger' => BeyondCode\LaravelWebSockets\Statistics\Logger\HttpStatisticsLogger::class,

    /*
     * Here you can specify the interval in seconds at which statistics should be logged.
     */
    'interval_in_seconds' => 60,

    /*
     * When the clean-command is executed, all recorded statistics older than
     * the number of days specified here will be deleted.
     */
    'delete_statistics_older_than_days' => 60,

    /*
     * Use an DNS resolver to make the requests to the statistics logger
     * default is to resolve everything to <IP_ADDRESS>.
     */
    'perform_dns_lookup' => false,
],

/*
 * Define the optional SSL context for your WebSocket connections.
 * You can see all available options at: http://php.net/manual/en/context.ssl.php
 */
'ssl' => [
    /*
     * Path to local certificate file on filesystem. It must be a PEM encoded file which
     * contains your certificate and private key. It can optionally contain the
     * certificate chain of issuers. The private key also may be contained
     * in a separate file specified by local_pk.
     */
    'local_cert' => env('LARAVEL_WEBSOCKETS_SSL_LOCAL_CERT', null),

    /*
     * Path to local private key file on filesystem in case of separate files for
     * certificate (local_cert) and private key.
     */
    'local_pk' => env('LARAVEL_WEBSOCKETS_SSL_LOCAL_PK', null),

    /*
     * Passphrase for your local_cert file.
     */
    'passphrase' => env('LARAVEL_WEBSOCKETS_SSL_PASSPHRASE', null),

    'verify_peer' => false,

    'allow_self_signed' => true,
],

/*
 * Channel Manager
 * This class handles how channel persistence is handled.
 * By default, persistence is stored in an array by the running webserver.
 * The only requirement is that the class should implement
 * `ChannelManager` interface provided by this package.
 */
'channel_manager' => \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManagers\ArrayChannelManager::class,

];
`
bootstrap.js
window.Echo = new Echo({
    broadcaster: 'pusher',
    key: process.env.MIX_PUSHER_APP_KEY,
    cluster: process.env.MIX_PUSHER_APP_CLUSTER,
    encrypted: true,
    wsHost: window.location.hostname,
    wsPort: 8443,
    wssPort: 8443,
    forceTLS: true,
    disableStats: false,
    enabledTransports: ['ws', 'wss']
});

.env
BROADCAST_DRIVER=pusher
APP_URL_BASE=DOMAIN_NAME_HERE
APP_URL=https://${APP_URL_BASE}
LARAVEL_WEBSOCKETS_SSL_LOCAL_CERT=/etc/letsencrypt/live/DIR_NAME_HERE/fullchain.pem
LARAVEL_WEBSOCKETS_SSL_LOCAL_PK=/etc/letsencrypt/live/DIR_NAME_HERE/privkey.pem

/etc/supervisorctl/conf.d/websockets.conf
[program:websockets]
process_name=%(program_name)s
command=sudo php artisan websockets:serve --port=8443
directory=/var/www/html/PROJECT_NAME_HERE
autostart=true
autorestart=true
user=root
redirect_stderr=true
stdout_logfile=/var/www/html/PROJECT_NAME_HERE/storage/logs/websockets.log

This worked for me
broadcasting.php
`
return [
/*
|--------------------------------------------------------------------------
| Default Broadcaster
|--------------------------------------------------------------------------
|
| This option controls the default broadcaster that will be used by the
| framework when an event needs to be broadcast. You may set this to
| any of the connections defined in the "connections" array below.
|
| Supported: "pusher", "redis", "log", "null"
|
*/

'default' => env('BROADCAST_DRIVER', 'null'),

/*
|--------------------------------------------------------------------------
| Broadcast Connections
|--------------------------------------------------------------------------
|
| Here you may define all of the broadcast connections that will be used
| to broadcast events to other systems or over websockets. Samples of
| each available type of connection are provided inside this array.
|
*/

'connections' => [

    'pusher' => [
        'driver' => 'pusher',
        'key' => env('PUSHER_APP_KEY'),
        'secret' => env('PUSHER_APP_SECRET'),
        'app_id' => env('PUSHER_APP_ID'),
        'options' => [
            'cluster' => env('PUSHER_APP_CLUSTER'),
            'encrypted' => true,
            'host' => '<IP_ADDRESS>',
            'port' => 8443,
            'scheme' => 'https',
            'curl_options' => [
                CURLOPT_SSL_VERIFYHOST => 0,
                CURLOPT_SSL_VERIFYPEER => 0,
            ]
        ],
    ],

    'redis' => [
        'driver' => 'redis',
        'connection' => 'default',
    ],

    'log' => [
        'driver' => 'log',
    ],

    'null' => [
        'driver' => 'null',
    ],

],

];
`
websockets.php
`
return [
/*
 * Set a custom dashboard configuration
 */
'dashboard' => [
    'port' => env('LARAVEL_WEBSOCKETS_PORT', 8443),
],

/*
 * This package comes with multi tenancy out of the box. Here you can
 * configure the different apps that can use the webSockets server.
 *
 * Optionally you specify capacity so you can limit the maximum
 * concurrent connections for a specific app.
 *
 * Optionally you can disable client events so clients cannot send
 * messages to each other via the webSockets.
 */
'apps' => [
    [
        'id' => env('PUSHER_APP_ID'),
        'name' => env('APP_NAME'),
        'key' => env('PUSHER_APP_KEY'),
        'secret' => env('PUSHER_APP_SECRET'),
        'path' => env('PUSHER_APP_PATH'),
        'capacity' => null,
        'enable_client_messages' => true,
        'enable_statistics' => true,
        'encrypted' => true,
    ],
],

/*
 * This class is responsible for finding the apps. The default provider
 * will use the apps defined in this config file.
 *
 * You can create a custom provider by implementing the
 * `AppProvider` interface.
 */
'app_provider' => BeyondCode\LaravelWebSockets\Apps\ConfigAppProvider::class,

/*
 * This array contains the hosts of which you want to allow incoming requests.
 * Leave this empty if you want to accept requests from all hosts.
 */
'allowed_origins' => [
    //
],

/*
 * The maximum request size in kilobytes that is allowed for an incoming WebSocket request.
 */
'max_request_size_in_kb' => 250,

/*
 * This path will be used to register the necessary routes for the package.
 */
'path' => 'laravel-websockets',

/*
 * Dashboard Routes Middleware
 *
 * These middleware will be assigned to every dashboard route, giving you
 * the chance to add your own middleware to this list or change any of
 * the existing middleware. Or, you can simply stick with this list.
 */
'middleware' => [
    'web',
    Authorize::class,
],

'statistics' => [
    /*
     * This model will be used to store the statistics of the WebSocketsServer.
     * The only requirement is that the model should extend
     * `WebSocketsStatisticsEntry` provided by this package.
     */
    'model' => \BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry::class,

    /**
     * The Statistics Logger will, by default, handle the incoming statistics, store them
     * and then release them into the database on each interval defined below.
     */
    'logger' => BeyondCode\LaravelWebSockets\Statistics\Logger\HttpStatisticsLogger::class,

    /*
     * Here you can specify the interval in seconds at which statistics should be logged.
     */
    'interval_in_seconds' => 60,

    /*
     * When the clean-command is executed, all recorded statistics older than
     * the number of days specified here will be deleted.
     */
    'delete_statistics_older_than_days' => 60,

    /*
     * Use an DNS resolver to make the requests to the statistics logger
     * default is to resolve everything to <IP_ADDRESS>.
     */
    'perform_dns_lookup' => false,
],

/*
 * Define the optional SSL context for your WebSocket connections.
 * You can see all available options at: http://php.net/manual/en/context.ssl.php
 */
'ssl' => [
    /*
     * Path to local certificate file on filesystem. It must be a PEM encoded file which
     * contains your certificate and private key. It can optionally contain the
     * certificate chain of issuers. The private key also may be contained
     * in a separate file specified by local_pk.
     */
    'local_cert' => env('LARAVEL_WEBSOCKETS_SSL_LOCAL_CERT', null),

    /*
     * Path to local private key file on filesystem in case of separate files for
     * certificate (local_cert) and private key.
     */
    'local_pk' => env('LARAVEL_WEBSOCKETS_SSL_LOCAL_PK', null),

    /*
     * Passphrase for your local_cert file.
     */
    'passphrase' => env('LARAVEL_WEBSOCKETS_SSL_PASSPHRASE', null),

    'verify_peer' => false,

    'allow_self_signed' => true,
],

/*
 * Channel Manager
 * This class handles how channel persistence is handled.
 * By default, persistence is stored in an array by the running webserver.
 * The only requirement is that the class should implement
 * `ChannelManager` interface provided by this package.
 */
'channel_manager' => \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManagers\ArrayChannelManager::class,

];
`
bootstrap.js
window.Echo = new Echo({
    broadcaster: 'pusher',
    key: process.env.MIX_PUSHER_APP_KEY,
    cluster: process.env.MIX_PUSHER_APP_CLUSTER,
    encrypted: true,
    wsHost: window.location.hostname,
    wsPort: 8443,
    wssPort: 8443,
    forceTLS: true,
    disableStats: false,
    enabledTransports: ['ws', 'wss']
});

.env
BROADCAST_DRIVER=pusher
APP_URL_BASE=DOMAIN_NAME_HERE
APP_URL=https://${APP_URL_BASE}
LARAVEL_WEBSOCKETS_SSL_LOCAL_CERT=/etc/letsencrypt/live/DIR_NAME_HERE/fullchain.pem
LARAVEL_WEBSOCKETS_SSL_LOCAL_PK=/etc/letsencrypt/live/DIR_NAME_HERE/privkey.pem

/etc/supervisorctl/conf.d/websockets.conf
[program:websockets]
process_name=%(program_name)s
command=sudo php artisan websockets:serve --port=8443
directory=/var/www/html/PROJECT_NAME_HERE
autostart=true
autorestart=true
user=root
redirect_stderr=true
stdout_logfile=/var/www/html/PROJECT_NAME_HERE/storage/logs/websockets.log

Check your port that it is not disabled.
ufw allow 6001 - open this port

Check your port that it is not disabled.
ufw allow 6001 - open this port

i am still stuck on laravel websockets please help

i spent 2 days reading every article and documentation, to figure out the problem, and i turned out to be using wrong port for ssl so just change the port in the broadcasting config file to ssl port and here is my config snippet
            'driver' => 'pusher',
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'app_id' => env('PUSHER_APP_ID'),
            'options' => [
                'cluster' => env('PUSHER_APP_CLUSTER'),
                'host' => 'socket.example.com',
                'port' => 443,
                'scheme' => 'https',
                'curl_options' => [
                    CURLOPT_SSL_VERIFYHOST => 0,
                    CURLOPT_SSL_VERIFYPEER => 0,
                ]
            ],

How to run websockets (php artisan websockets:serve) that default port is 6001 ?

to enable secure websocket (wss) in localhost (tested in laragon windows 10 laravel 8) (self signed certificate)

generate a new certificate (in laragon windows 10 this should be stored in  C:\laragon\etc\ssl)openssl req -new -x509 -days 36500 -nodes -newkey rsa:2048 -keyout wss.key -out wss.crt -extensions v3_req

use certificate as your domain ssl (apache domain.conf file)SSLEngine on
SSLCertificateFile      C:/laragon/etc/ssl/wss.crt
SSLCertificateKeyFile   C:/laragon/etc/ssl/wss.key

point environment to the generated certificate (.env file)LARAVEL_WEBSOCKETS_SSL_LOCAL_CERT=C:/laragon/etc/ssl/wss.crt
LARAVEL_WEBSOCKETS_SSL_LOCAL_PK=C:/laragon/etc/ssl/wss.key

update config/websockets.php'ssl' => [
    ...
    'verify_peer' => false,
],

update config/broadcasting.php'pusher' => [
   ...
   'options' => [
       ...
       'scheme' => 'https',
       'curl_options' => [
           CURLOPT_SSL_VERIFYHOST => 0,
           CURLOPT_SSL_VERIFYPEER => 0,
       ],
   ],
],

add this code in Echo (resources/js/bootstrap.js)window.Echo = new Echo({
 ...
 wssPort: 6001,
 ...
});

clear config cache then run websocket:serve php artisan config:cache&&php artisan websockets:serve

Hello guys, i was struggling with this error for 2 days, the solution was too simple.
just change the ports to 443 and 80.

I followed these steps, the problem in my case was the SSL quotes in the .ENV and the impossibility to get my FREE SSL to work due to a change in names every 3 months.
https://kollox.com/how-to-set-up-laravel-websockets-with-a-self-signed-ssl-certificate/

This worked for me
broadcasting.php
`
return [
/*
|--------------------------------------------------------------------------
| Default Broadcaster
|--------------------------------------------------------------------------
|
| This option controls the default broadcaster that will be used by the
| framework when an event needs to be broadcast. You may set this to
| any of the connections defined in the "connections" array below.
|
| Supported: "pusher", "redis", "log", "null"
|
*/

'default' => env('BROADCAST_DRIVER', 'null'),

/*
|--------------------------------------------------------------------------
| Broadcast Connections
|--------------------------------------------------------------------------
|
| Here you may define all of the broadcast connections that will be used
| to broadcast events to other systems or over websockets. Samples of
| each available type of connection are provided inside this array.
|
*/

'connections' => [

    'pusher' => [
        'driver' => 'pusher',
        'key' => env('PUSHER_APP_KEY'),
        'secret' => env('PUSHER_APP_SECRET'),
        'app_id' => env('PUSHER_APP_ID'),
        'options' => [
            'cluster' => env('PUSHER_APP_CLUSTER'),
            'encrypted' => true,
            'host' => '<IP_ADDRESS>',
            'port' => 8443,
            'scheme' => 'https',
            'curl_options' => [
                CURLOPT_SSL_VERIFYHOST => 0,
                CURLOPT_SSL_VERIFYPEER => 0,
            ]
        ],
    ],

    'redis' => [
        'driver' => 'redis',
        'connection' => 'default',
    ],

    'log' => [
        'driver' => 'log',
    ],

    'null' => [
        'driver' => 'null',
    ],

],

];
`
websockets.php
`
return [
/*
 * Set a custom dashboard configuration
 */
'dashboard' => [
    'port' => env('LARAVEL_WEBSOCKETS_PORT', 8443),
],

/*
 * This package comes with multi tenancy out of the box. Here you can
 * configure the different apps that can use the webSockets server.
 *
 * Optionally you specify capacity so you can limit the maximum
 * concurrent connections for a specific app.
 *
 * Optionally you can disable client events so clients cannot send
 * messages to each other via the webSockets.
 */
'apps' => [
    [
        'id' => env('PUSHER_APP_ID'),
        'name' => env('APP_NAME'),
        'key' => env('PUSHER_APP_KEY'),
        'secret' => env('PUSHER_APP_SECRET'),
        'path' => env('PUSHER_APP_PATH'),
        'capacity' => null,
        'enable_client_messages' => true,
        'enable_statistics' => true,
        'encrypted' => true,
    ],
],

/*
 * This class is responsible for finding the apps. The default provider
 * will use the apps defined in this config file.
 *
 * You can create a custom provider by implementing the
 * `AppProvider` interface.
 */
'app_provider' => BeyondCode\LaravelWebSockets\Apps\ConfigAppProvider::class,

/*
 * This array contains the hosts of which you want to allow incoming requests.
 * Leave this empty if you want to accept requests from all hosts.
 */
'allowed_origins' => [
    //
],

/*
 * The maximum request size in kilobytes that is allowed for an incoming WebSocket request.
 */
'max_request_size_in_kb' => 250,

/*
 * This path will be used to register the necessary routes for the package.
 */
'path' => 'laravel-websockets',

/*
 * Dashboard Routes Middleware
 *
 * These middleware will be assigned to every dashboard route, giving you
 * the chance to add your own middleware to this list or change any of
 * the existing middleware. Or, you can simply stick with this list.
 */
'middleware' => [
    'web',
    Authorize::class,
],

'statistics' => [
    /*
     * This model will be used to store the statistics of the WebSocketsServer.
     * The only requirement is that the model should extend
     * `WebSocketsStatisticsEntry` provided by this package.
     */
    'model' => \BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry::class,

    /**
     * The Statistics Logger will, by default, handle the incoming statistics, store them
     * and then release them into the database on each interval defined below.
     */
    'logger' => BeyondCode\LaravelWebSockets\Statistics\Logger\HttpStatisticsLogger::class,

    /*
     * Here you can specify the interval in seconds at which statistics should be logged.
     */
    'interval_in_seconds' => 60,

    /*
     * When the clean-command is executed, all recorded statistics older than
     * the number of days specified here will be deleted.
     */
    'delete_statistics_older_than_days' => 60,

    /*
     * Use an DNS resolver to make the requests to the statistics logger
     * default is to resolve everything to <IP_ADDRESS>.
     */
    'perform_dns_lookup' => false,
],

/*
 * Define the optional SSL context for your WebSocket connections.
 * You can see all available options at: http://php.net/manual/en/context.ssl.php
 */
'ssl' => [
    /*
     * Path to local certificate file on filesystem. It must be a PEM encoded file which
     * contains your certificate and private key. It can optionally contain the
     * certificate chain of issuers. The private key also may be contained
     * in a separate file specified by local_pk.
     */
    'local_cert' => env('LARAVEL_WEBSOCKETS_SSL_LOCAL_CERT', null),

    /*
     * Path to local private key file on filesystem in case of separate files for
     * certificate (local_cert) and private key.
     */
    'local_pk' => env('LARAVEL_WEBSOCKETS_SSL_LOCAL_PK', null),

    /*
     * Passphrase for your local_cert file.
     */
    'passphrase' => env('LARAVEL_WEBSOCKETS_SSL_PASSPHRASE', null),

    'verify_peer' => false,

    'allow_self_signed' => true,
],

/*
 * Channel Manager
 * This class handles how channel persistence is handled.
 * By default, persistence is stored in an array by the running webserver.
 * The only requirement is that the class should implement
 * `ChannelManager` interface provided by this package.
 */
'channel_manager' => \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManagers\ArrayChannelManager::class,

];
`
bootstrap.js
window.Echo = new Echo({
    broadcaster: 'pusher',
    key: process.env.MIX_PUSHER_APP_KEY,
    cluster: process.env.MIX_PUSHER_APP_CLUSTER,
    encrypted: true,
    wsHost: window.location.hostname,
    wsPort: 8443,
    wssPort: 8443,
    forceTLS: true,
    disableStats: false,
    enabledTransports: ['ws', 'wss']
});

.env
BROADCAST_DRIVER=pusher
APP_URL_BASE=DOMAIN_NAME_HERE
APP_URL=https://${APP_URL_BASE}
LARAVEL_WEBSOCKETS_SSL_LOCAL_CERT=/etc/letsencrypt/live/DIR_NAME_HERE/fullchain.pem
LARAVEL_WEBSOCKETS_SSL_LOCAL_PK=/etc/letsencrypt/live/DIR_NAME_HERE/privkey.pem

/etc/supervisorctl/conf.d/websockets.conf
[program:websockets]
process_name=%(program_name)s
command=sudo php artisan websockets:serve --port=8443
directory=/var/www/html/PROJECT_NAME_HERE
autostart=true
autorestart=true
user=root
redirect_stderr=true
stdout_logfile=/var/www/html/PROJECT_NAME_HERE/storage/logs/websockets.log

Finally it worked! Thanks

I have tried everything as all of you comment here but unfortunately, it does not work on production machine (LEMP). I was wondering if someone could help us to figure it out and we would pay for your help. Thank you.

I have tried everything as all of you comment here but unfortunately, it does not work on production machine (LEMP) on my LAN. I was wondering if someone could help us to figure it out and we would pay for your help. Please note that it works on my local development PC (WAMP) with ssl. Thank you.
1- Broadcasting.php file:
'connections' => [
'pusher' => [
    'driver' => 'pusher',
    'key' => env('PUSHER_APP_KEY'),
    'secret' => env('PUSHER_APP_SECRET'),
    'app_id' => env('PUSHER_APP_ID'),
    'options' => [
        'cluster' => env('PUSHER_APP_CLUSTER'),
        'encrypted' => true,
        'host' => '<IP_ADDRESS>',
        'port' => 443,
        'scheme' => 'https',
        'curl_options' => [
            CURLOPT_SSL_VERIFYHOST => 0,
            CURLOPT_SSL_VERIFYPEER => 0,
        ]
    ],
],

2- Nginx virtual host configuration for proxy
location /app/ {
proxy_pass http://<IP_ADDRESS>:6001/app/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}
location /apps/ {
    proxy_pass http://<IP_ADDRESS>:6001/apps/;
    proxy_set_header Host $host;
}

3- Front-end
window.Echo = new Echo({
broadcaster: 'pusher',
key: process.env.MIX_PUSHER_APP_KEY,
cluster: process.env.MIX_PUSHER_APP_CLUSTER,
wsHost: window.location.hostname,
encrypted: true,
wssPort: 443, // Https port
disableStats: true, // Change this to your liking this disables statistics
forceTLS: true,
enabledTransports: ['ws', 'wss'],
});
4- .env file:
LARAVEL_WEBSOCKETS_SSL_LOCAL_CERT=/etc/nginx/ssl/letsencrypt/demo.angkorhospital.org/ssl.crt
LARAVEL_WEBSOCKETS_SSL_LOCAL_PK=/etc/nginx/ssl/letsencrypt/demo.angkorhospital.org/ssl.key
5- Websocket running on port 6001 with user root

@sopheary sure I'd like to help. Hit me up on<EMAIL_ADDRESS>

I have running 2 VPS servers with Laravel Websockets with SSL in apache, it was a battle to get it to work.
The main problem is letsencrypt, after several hours of testing, I decided to completely remove letsencrypt and reinstall it, once it was re-installed it proceeded to generate the certificates again and it worked the first time.
First Server

Second Server

broadcasting.php file:
    'pusher' => [
      'driver' => 'pusher',
      'key' => env('PUSHER_APP_KEY'),
      'secret' => env('PUSHER_APP_SECRET'),
      'app_id' => env('PUSHER_APP_ID'),
      'options' => [
        'cluster' => env('PUSHER_APP_CLUSTER'),
        'encrypted' => TRUE,
        'useTLS' => TRUE,
        'host' => 'homeflowapp.com',
        'port' => 6001,
        'scheme' => 'https',
        'curl_options' => [
          CURLOPT_SSL_VERIFYHOST => 0,
          CURLOPT_SSL_VERIFYPEER => 0,
        ],
      ],
    ],

websockets.php
'apps' => [
        [
            'id' => env('PUSHER_APP_ID'),
            'name' => env('APP_NAME'),
            'host' => env('PUSHER_APP_HOST'),
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'path' => env('PUSHER_APP_PATH'),
            'capacity' => null,
            'enable_client_messages' => false,
            'enable_statistics' => true,
            'allowed_origins' => [
                // env('LARAVEL_WEBSOCKETS_DOMAIN'),
            ],
        ],
    ],

Uninstall Let's Encrypt
sudo apt remove certbot python3-certbot-apache
sudo rm -rf /etc/letsencrypt

Delete 000-default-le-ssl.conf that letsencrypt generates from /etc/apache2/sites-available and from /etc/apache2/sites-enabled
Remove the following lines from 000-default.conf from /etc/apache2/sites-available
RewriteEngine on
RewriteCond %{SERVER_NAME} =homeflowapp.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]

Install Let's Encrypt
sudo apt install certbot python3-certbot-apache
sudo certbot --apache

Add websockets.conf in supervisor:
[program:websockets]
command=/usr/bin/php /var/www/html/homeflow/artisan websockets:serve
numprocs=1
autostart=true
autorestart=true
user=root

@sopheary I'll do a test with Nginx to see how it goes.

I have running 2 VPS servers with Laravel Websockets with SSL in apache, it was a battle to get it to work.
The main problem is letsencrypt, after several hours of testing, I decided to completely remove letsencrypt and reinstall it, once it was re-installed it proceeded to generate the certificates again and it worked the first time.
First Server

Second Server

broadcasting.php file:
    'pusher' => [
      'driver' => 'pusher',
      'key' => env('PUSHER_APP_KEY'),
      'secret' => env('PUSHER_APP_SECRET'),
      'app_id' => env('PUSHER_APP_ID'),
      'options' => [
        'cluster' => env('PUSHER_APP_CLUSTER'),
        'encrypted' => TRUE,
        'useTLS' => TRUE,
        'host' => 'homeflowapp.com',
        'port' => 6001,
        'scheme' => 'https',
        'curl_options' => [
          CURLOPT_SSL_VERIFYHOST => 0,
          CURLOPT_SSL_VERIFYPEER => 0,
        ],
      ],
    ],

websockets.php
'apps' => [
        [
            'id' => env('PUSHER_APP_ID'),
            'name' => env('APP_NAME'),
            'host' => env('PUSHER_APP_HOST'),
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'path' => env('PUSHER_APP_PATH'),
            'capacity' => null,
            'enable_client_messages' => false,
            'enable_statistics' => true,
            'allowed_origins' => [
                // env('LARAVEL_WEBSOCKETS_DOMAIN'),
            ],
        ],
    ],

SSL
'ssl' => [

        'local_cert' => '/etc/letsencrypt/live/homeflowapp.com/fullchain.pem',

        'capath' => env('LARAVEL_WEBSOCKETS_SSL_CA', null),

        'local_pk' => '/etc/letsencrypt/live/homeflowapp.com/privkey.pem',

        'passphrase' => env('LARAVEL_WEBSOCKETS_SSL_PASSPHRASE', null),

        'verify_peer' => FALSE,

        'allow_self_signed' => FALSE,

    ],

Uninstall Let's Encrypt
sudo apt remove certbot python3-certbot-apache
sudo rm -rf /etc/letsencrypt

Delete 000-default-le-ssl.conf that letsencrypt generates from /etc/apache2/sites-available and from /etc/apache2/sites-enabled
Remove the following lines from 000-default.conf from /etc/apache2/sites-available
RewriteEngine on
RewriteCond %{SERVER_NAME} =homeflowapp.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]

Install Let's Encrypt
sudo apt install certbot python3-certbot-apache
sudo certbot --apache

Add websockets.conf in supervisor:
[program:websockets]
command=/usr/bin/php /var/www/html/homeflow/artisan websockets:serve
numprocs=1
autostart=true
autorestart=true
user=root

@sopheary I'll do a test with Nginx to see how it goes.

Yeah, this is exposing the websockets directly, the other option was using a reverse proxy. Either way it works for me both apacge2 and nginx

I have running 2 VPS servers with Laravel Websockets with SSL in apache, it was a battle to get it to work.
The main problem is letsencrypt, after several hours of testing, I decided to completely remove letsencrypt and reinstall it, once it was re-installed it proceeded to generate the certificates again and it worked the first time.
First Server

Second Server

broadcasting.php file:
    'pusher' => [
      'driver' => 'pusher',
      'key' => env('PUSHER_APP_KEY'),
      'secret' => env('PUSHER_APP_SECRET'),
      'app_id' => env('PUSHER_APP_ID'),
      'options' => [
        'cluster' => env('PUSHER_APP_CLUSTER'),
        'encrypted' => TRUE,
        'useTLS' => TRUE,
        'host' => 'homeflowapp.com',
        'port' => 6001,
        'scheme' => 'https',
        'curl_options' => [
          CURLOPT_SSL_VERIFYHOST => 0,
          CURLOPT_SSL_VERIFYPEER => 0,
        ],
      ],
    ],

websockets.php
'apps' => [
        [
            'id' => env('PUSHER_APP_ID'),
            'name' => env('APP_NAME'),
            'host' => env('PUSHER_APP_HOST'),
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'path' => env('PUSHER_APP_PATH'),
            'capacity' => null,
            'enable_client_messages' => false,
            'enable_statistics' => true,
            'allowed_origins' => [
                // env('LARAVEL_WEBSOCKETS_DOMAIN'),
            ],
        ],
    ],

SSL
'ssl' => [

        'local_cert' => '/etc/letsencrypt/live/homeflowapp.com/fullchain.pem',

        'capath' => env('LARAVEL_WEBSOCKETS_SSL_CA', null),

        'local_pk' => '/etc/letsencrypt/live/homeflowapp.com/privkey.pem',

        'passphrase' => env('LARAVEL_WEBSOCKETS_SSL_PASSPHRASE', null),

        'verify_peer' => FALSE,

        'allow_self_signed' => FALSE,

    ],

Uninstall Let's Encrypt
sudo apt remove certbot python3-certbot-apache
sudo rm -rf /etc/letsencrypt

Delete 000-default-le-ssl.conf that letsencrypt generates from /etc/apache2/sites-available and from /etc/apache2/sites-enabled
Remove the following lines from 000-default.conf from /etc/apache2/sites-available
RewriteEngine on
RewriteCond %{SERVER_NAME} =homeflowapp.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]

Install Let's Encrypt
sudo apt install certbot python3-certbot-apache
sudo certbot --apache

Add websockets.conf in supervisor:
[program:websockets]
command=/usr/bin/php /var/www/html/homeflow/artisan websockets:serve
numprocs=1
autostart=true
autorestart=true
user=root

@sopheary I'll do a test with Nginx to see how it goes.

Thank you so much for your reply with a detail configuration. In my case, I am using the reverse proxy option and I found the problem caused by Laravel (I am using Laravel v8). I will need to run the below commands to make laravel-websockets works and those commands will be executed every time my production server receives a new changes from master branch.
php artisan clear-compiled
php artisan optimize
I was wondering what version of laravel-websocket do you use? Its interface is different from mine. My laravel-websocket is version 1.12.
By the way, my laravel-websocket works now but unfortunately, its real time graph does not show up anything. In the configuration, I already did 'enable_statistics' => true.

@sopheary did you manage to get it working? The statistics that is.

Yes it works now but only real time statistics does not work.

@sopheary lograste que funcionara? Las estadísticas que son.

Sí, funciona ahora, pero solo las estadísticas en tiempo real no funcionan.

You have enabled 'enable_statistics' => true, in websockets.php ?

@sopheary did you manage to get it working? The statistics that is.

Yes it works now but only real time statistics does not work.

You have enabled 'enable_statistics' => true, in websockets.php ?

@sopheary I have version 2.

@sopheary I have version 2.

Thank you for your reply. I was wondering how to send message back to server or retrieve records from database over the existing websocket connection? For now what I did, used ajax to retrieve records to update the client interface when the client receive a broadcast event from the server. I know this is an anther topic that I should not ask it here but you might help me here or refer to the a tutorial link somewhere. I have tried to search on this topic but not found one.

@sopheary did you manage to get it working? The statistics that is.

Yes it works now but only real time statistics does not work.

You have enabled 'enable_statistics' => true, in websockets.php ?

Yes I did enable it but still does not work.

@sopheary did you manage to get it working? The statistics that is.

Yes it works now but only real time statistics does not work.

You have enabled 'enable_statistics' => true, in websockets.php ?

Yes I did enable it but still does not work.

I suggest you watch this guy's courses, it will help you a lot, I am forwarding you to the entire series:
https://www.youtube.com/c/codecourse/search?query=websocket


[Question] Training with Qwen2 backend got loss 0

Question
I got loss to be 0 when training on Qwen2 backend,
{'loss': 0.0, 'learning_rate': 0.00015267175572519084, 'epoch': 0.0}
0%|▎                                                                                | 20/8720 [01:38<11:01:39,  4.56s/it]WARNING: tokenization mismatch: 47 vs. 48. (ignored)
WARNING: tokenization mismatch: 54 vs. 55. (ignored)
WARNING: tokenization mismatch: 46 vs. 47. (ignored)
WARNING: tokenization mismatch: 43 vs. 44. (ignored)
What could be the reason caused it?

me too

I found that the reason for this problem is different tokenizer rules. The bos_token is null and the eos_token is set to "<|endoftext|>" in the Qwen tokenizer configuration. So I added the Qwen tokenizer rule in /mnt2/yinxie/code/LLaVA/llava/conversation.py as follows:
class SeparatorStyle(Enum):
    """Different separator style."""
    SINGLE = auto()
    TWO = auto()
    MPT = auto()
    PLAIN = auto()
    LLAMA_2 = auto()
    QWEN_2 = auto()
def get_prompt(self):
   elif self.sep_style == SeparatorStyle.QWEN_2:
            seps = [self.sep, self.sep2]
            ret = self.system + seps[0]
            for i, (role, message) in enumerate(messages):
                if message:
                    if type(message) is tuple:
                        message, _, _ = message
                    ret += role + ": " + message + seps[i % 2]
                else:
                    ret += role + ":"

conv_qwen_2 = Conversation(
    system="A chat between a curious user and an artificial intelligence assistant. "
    "The assistant gives helpful, detailed, and polite answers to the user's questions.",
    roles=("USER", "ASSISTANT"),
    version="qwen_v2",
    messages=(),
    offset=0,
    sep_style=SeparatorStyle.QWEN_2,
    sep=" ",
    sep2="<|endoftext|>",
)
conv_templates = {
    "default": conv_vicuna_v0,
    "v0": conv_vicuna_v0,
    "v1": conv_vicuna_v1,
    "vicuna_v1": conv_vicuna_v1,
    "qwen_2": conv_qwen_2,
    "llama_2": conv_llama_2,
    "mistral_instruct": conv_mistral_instruct,
    "chatml_direct": conv_chatml_direct,
    "mistral_direct": conv_chatml_direct,

    "plain": conv_llava_plain,
    "v0_plain": conv_llava_plain,
    "llava_v0": conv_llava_v0,
    "v0_mmtag": conv_llava_v0_mmtag,
    "llava_v1": conv_llava_v1,
    "v1_mmtag": conv_llava_v1_mmtag,
    "llava_llama_2": conv_llava_llama_2,

    "mpt": conv_mpt,
}

And then, I added the method preprocess_qwen_2 in train.py.
def preprocess_qwen_2(
    sources,
    tokenizer: transformers.PreTrainedTokenizer,
    has_image: bool = False
) -> Dict:
    conv = conversation_lib.default_conversation.copy()
    roles = {"human": conv.roles[0], "gpt": conv.roles[1]}

    # Apply prompt templates
    conversations = []
    for i, source in enumerate(sources):
        if roles[source[0]["from"]] != conv.roles[0]:
            # Skip the first one if it is not from human
            source = source[1:]

        conv.messages = []
        for j, sentence in enumerate(source):
            role = roles[sentence["from"]]
            assert role == conv.roles[j % 2], f"{i}"
            conv.append_message(role, sentence["value"])
        conversations.append(conv.get_prompt())

    # Tokenize conversations

    if has_image:
        input_ids = torch.stack([tokenizer_image_token(prompt, tokenizer, return_tensors='pt') for prompt in conversations], dim=0)
    else:
        input_ids = tokenizer(
            conversations,
            return_tensors="pt",
            padding="longest",
            max_length=tokenizer.model_max_length,
            truncation=True,
        ).input_ids

    targets = input_ids.clone()

    assert conv.sep_style == conversation_lib.SeparatorStyle.QWEN_2

    # Mask targets
    sep = conv.sep + conv.roles[1] + ": "
    for conversation, target in zip(conversations, targets):
        total_len = int(target.ne(tokenizer.pad_token_id).sum())

        rounds = conversation.split(conv.sep2)
        rounds_len = len(rounds)
        cur_len = 0
        # target[:cur_len] = IGNORE_INDEX
        for i, rou in enumerate(rounds):
            if rou == "":
                break

            parts = rou.split(sep)
            if len(parts) != 2:
                break
            parts[0] += sep

            if has_image:
                round_ids = tokenizer_image_token(rou, tokenizer)
                instruction_ids = tokenizer_image_token(parts[0], tokenizer)
                equal_parts = [x == y for x, y in zip(round_ids, instruction_ids)]

                instruction_len = equal_parts.index(False) if False in equal_parts else len(equal_parts)
                round_len = len(round_ids)

            else:
                round_ids = tokenizer(rou).input_ids
                instruction_ids = tokenizer(parts[0]).input_ids
                equal_parts = [x == y for x, y in zip(round_ids, instruction_ids)]

                instruction_len = equal_parts.index(False) if False in equal_parts else len(equal_parts)
                round_len = len(round_ids)

            if i != 0 and not tokenizer.legacy and IS_TOKENIZER_GREATER_THAN_0_14:
                round_len += 1
                instruction_len += 1

            target[cur_len : cur_len + instruction_len] = IGNORE_INDEX

            cur_len += round_len
        target[cur_len:] = IGNORE_INDEX

        if cur_len < tokenizer.model_max_length:
            if cur_len != total_len + rounds_len - 2:
                target[:] = IGNORE_INDEX
                print(
                    f"WARNING: tokenization mismatch: {cur_len} vs. {total_len}."
                    f" (ignored)"
                )

    return dict(
        input_ids=input_ids,
        labels=targets,
    )

def preprocess(
    sources: Sequence[str],
    tokenizer: transformers.PreTrainedTokenizer,
    has_image: bool = False
) -> Dict:
    if conversation_lib.default_conversation.sep_style == conversation_lib.SeparatorStyle.PLAIN:
        return preprocess_plain(sources, tokenizer)
    if conversation_lib.default_conversation.sep_style == conversation_lib.SeparatorStyle.LLAMA_2:
        return preprocess_llama_2(sources, tokenizer, has_image=has_image)
    if conversation_lib.default_conversation.version.startswith("v1"):
        return preprocess_v1(sources, tokenizer, has_image=has_image)
    if conversation_lib.default_conversation.version == "mpt":
        return preprocess_mpt(sources, tokenizer, has_image=has_image)
    if conversation_lib.default_conversation.version.startswith("qwen_v2"):
        return preprocess_qwen_2(sources, tokenizer, has_image=has_image)

After these operations, the mismatch warning disappeared.
However, I must mention that I don't have GPUs for training now, so there may be other problems.
Hope this helps you.

Okay, after making this change, I trained the model and the loss appears to be normal and mismatch warning disappeared. I trained the MM adapter from scratch and pretrained LLM of Qwen_7B.

@yiyexy  hello, nice catch. Am training normal now.
Did u trained on llava pretrain data? Does there any pretrain data could be used for Chinese enhancement ?

@yiyexy hello, nice catch. Am training normal now. Did u trained on llava pretrain data? Does there any pretrain data could be used for Chinese enhancement ?

Yes, I trained on LLaMA pretrain data. Unfortunately, I don't have data to enhance the model's capability in Chinese. By the way, I'm currently developing a new data processing pipeline which may solve this problem one day.

@yiyexy Will u consider share your processing pipeline? Which part problem to solve? There are some Chinese data but I think their quality is poor.

@lucasjinreal  I will. But it still has some problems to be solved. It's a long way.

@yiyexy Hello, Your loss looks not like stage 1?
BTW, you probably should use qwen1.5-7b-chat model. Otherwise you can not sft efficiently.
However, qwen using chatml chat format, not llava default.
How do u change it?

I found that the reason for this problem is different tokenizer rules. The bos_token is null and the eos_token is set to "<|endoftext|>" in the Qwen tokenizer configuration. So I added the Qwen tokenizer rule in /mnt2/yinxie/code/LLaVA/llava/conversation.py as follows:
class SeparatorStyle(Enum):
    """Different separator style."""
    SINGLE = auto()
    TWO = auto()
    MPT = auto()
    PLAIN = auto()
    LLAMA_2 = auto()
    QWEN_2 = auto()
def get_prompt(self):
   elif self.sep_style == SeparatorStyle.QWEN_2:
            seps = [self.sep, self.sep2]
            ret = self.system + seps[0]
            for i, (role, message) in enumerate(messages):
                if message:
                    if type(message) is tuple:
                        message, _, _ = message
                    ret += role + ": " + message + seps[i % 2]
                else:
                    ret += role + ":"

conv_qwen_2 = Conversation(
    system="A chat between a curious user and an artificial intelligence assistant. "
    "The assistant gives helpful, detailed, and polite answers to the user's questions.",
    roles=("USER", "ASSISTANT"),
    version="qwen_v2",
    messages=(),
    offset=0,
    sep_style=SeparatorStyle.QWEN_2,
    sep=" ",
    sep2="<|endoftext|>",
)
conv_templates = {
    "default": conv_vicuna_v0,
    "v0": conv_vicuna_v0,
    "v1": conv_vicuna_v1,
    "vicuna_v1": conv_vicuna_v1,
    "qwen_2": conv_qwen_2,
    "llama_2": conv_llama_2,
    "mistral_instruct": conv_mistral_instruct,
    "chatml_direct": conv_chatml_direct,
    "mistral_direct": conv_chatml_direct,

    "plain": conv_llava_plain,
    "v0_plain": conv_llava_plain,
    "llava_v0": conv_llava_v0,
    "v0_mmtag": conv_llava_v0_mmtag,
    "llava_v1": conv_llava_v1,
    "v1_mmtag": conv_llava_v1_mmtag,
    "llava_llama_2": conv_llava_llama_2,

    "mpt": conv_mpt,
}

And then, I added the method preprocess_qwen_2 in train.py.
def preprocess_qwen_2(
    sources,
    tokenizer: transformers.PreTrainedTokenizer,
    has_image: bool = False
) -> Dict:
    conv = conversation_lib.default_conversation.copy()
    roles = {"human": conv.roles[0], "gpt": conv.roles[1]}

    # Apply prompt templates
    conversations = []
    for i, source in enumerate(sources):
        if roles[source[0]["from"]] != conv.roles[0]:
            # Skip the first one if it is not from human
            source = source[1:]

        conv.messages = []
        for j, sentence in enumerate(source):
            role = roles[sentence["from"]]
            assert role == conv.roles[j % 2], f"{i}"
            conv.append_message(role, sentence["value"])
        conversations.append(conv.get_prompt())

    # Tokenize conversations

    if has_image:
        input_ids = torch.stack([tokenizer_image_token(prompt, tokenizer, return_tensors='pt') for prompt in conversations], dim=0)
    else:
        input_ids = tokenizer(
            conversations,
            return_tensors="pt",
            padding="longest",
            max_length=tokenizer.model_max_length,
            truncation=True,
        ).input_ids

    targets = input_ids.clone()

    assert conv.sep_style == conversation_lib.SeparatorStyle.QWEN_2

    # Mask targets
    sep = conv.sep + conv.roles[1] + ": "
    for conversation, target in zip(conversations, targets):
        total_len = int(target.ne(tokenizer.pad_token_id).sum())

        rounds = conversation.split(conv.sep2)
        rounds_len = len(rounds)
        cur_len = 0
        # target[:cur_len] = IGNORE_INDEX
        for i, rou in enumerate(rounds):
            if rou == "":
                break

            parts = rou.split(sep)
            if len(parts) != 2:
                break
            parts[0] += sep

            if has_image:
                round_ids = tokenizer_image_token(rou, tokenizer)
                instruction_ids = tokenizer_image_token(parts[0], tokenizer)
                equal_parts = [x == y for x, y in zip(round_ids, instruction_ids)]

                instruction_len = equal_parts.index(False) if False in equal_parts else len(equal_parts)
                round_len = len(round_ids)

            else:
                round_ids = tokenizer(rou).input_ids
                instruction_ids = tokenizer(parts[0]).input_ids
                equal_parts = [x == y for x, y in zip(round_ids, instruction_ids)]

                instruction_len = equal_parts.index(False) if False in equal_parts else len(equal_parts)
                round_len = len(round_ids)

            if i != 0 and not tokenizer.legacy and IS_TOKENIZER_GREATER_THAN_0_14:
                round_len += 1
                instruction_len += 1

            target[cur_len : cur_len + instruction_len] = IGNORE_INDEX

            cur_len += round_len
        target[cur_len:] = IGNORE_INDEX

        if cur_len < tokenizer.model_max_length:
            if cur_len != total_len + rounds_len - 2:
                target[:] = IGNORE_INDEX
                print(
                    f"WARNING: tokenization mismatch: {cur_len} vs. {total_len}."
                    f" (ignored)"
                )

    return dict(
        input_ids=input_ids,
        labels=targets,
    )

def preprocess(
    sources: Sequence[str],
    tokenizer: transformers.PreTrainedTokenizer,
    has_image: bool = False
) -> Dict:
    if conversation_lib.default_conversation.sep_style == conversation_lib.SeparatorStyle.PLAIN:
        return preprocess_plain(sources, tokenizer)
    if conversation_lib.default_conversation.sep_style == conversation_lib.SeparatorStyle.LLAMA_2:
        return preprocess_llama_2(sources, tokenizer, has_image=has_image)
    if conversation_lib.default_conversation.version.startswith("v1"):
        return preprocess_v1(sources, tokenizer, has_image=has_image)
    if conversation_lib.default_conversation.version == "mpt":
        return preprocess_mpt(sources, tokenizer, has_image=has_image)
    if conversation_lib.default_conversation.version.startswith("qwen_v2"):
        return preprocess_qwen_2(sources, tokenizer, has_image=has_image)

After these operations, the mismatch warning disappeared.
However, I must mention that I don't have GPUs for training now, so there may be other problems.
Hope this helps you.

Okay, after making this change, I trained the model and the loss appears to be normal and mismatch warning disappeared. I trained the MM adapter from scratch and pretrained LLM of Qwen_7B.

Hi,I hope to replace LLM with Qwen, and I have added it according to your code, but encountered the following error. How can I resolve this?
Original Traceback (most recent call last):
File "/root/miniconda3/lib/python3.10/site-packages/torch/utils/data/_utils/worker.py", line 308, in _worker_loop
data = fetcher.fetch(index)
File "/root/miniconda3/lib/python3.10/site-packages/torch/utils/data/_utils/fetch.py", line 51, in fetch
data = [self.dataset[idx] for idx in possibly_batched_index]
File "/root/miniconda3/lib/python3.10/site-packages/torch/utils/data/_utils/fetch.py", line 51, in
data = [self.dataset[idx] for idx in possibly_batched_index]
File "/root/LLaVA/llava/train/train.py", line 821, in getitem
data_dict = preprocess(
File "/root/LLaVA/llava/train/train.py", line 726, in preprocess
return preprocess_qwen_2(sources, tokenizer, has_image=has_image)
File "/root/LLaVA/llava/train/train.py", line 652, in preprocess_qwen_2
total_len = int(target.ne(tokenizer.pad_token_id).sum())
TypeError: ne() received an invalid combination of arguments - got (NoneType), but expected one of:

(Tensor other)
didn't match because some of the arguments have invalid types: (NoneType)
(Number other)
didn't match because some of the arguments have invalid types: (NoneType)

@yiyexy Hello, Your loss looks not like stage 1?
BTW, you probably should use qwen1.5-7b-chat model. Otherwise you can not sft efficiently.
However, qwen using chatml chat format, not llava default.
How do u change it?

You are right. The loss is stage 2.
And I use qwen1.5-7b-chat model for this stage.
BTW, I didn't meet problem with the format.
The SFT training is normal. Maybe I ignored some things.

@20191864218 Maybe you need set some parameters for Qwen1.5. https://github.com/haotian-liu/LLaVA/pull/1146

@yiyexy  Using llava template on qwen chat model might introduce unwanted output when chat. This is a common issue. qwen using chatml format which using <|im_end|> as spepartor/

@yiyexy Using llava template on qwen chat model might introduce unwanted output when chat. This is a common issue. qwen using chatml format which using <|im_end|> as spepartor/

Thanks for your reminder. I will pay attention to this issue. I haven't trained a llava-qwen model due to a lack of GPU resources and other work commitments.
I will train a llava-qwen model as soon as possible and share the result with you.

@yiyexy  Thank u. Am doing finetune stage now. Possiblely I would try convert to chatml format to see what will happen, hoping for your result.

@20191864218 Maybe you need set some parameters for Qwen1.5. #1146

Thank you, but I've encountered some issues after making the changes. Could you help me with it?
I left a comment on the link you provided.

@yiyexy 感谢你。现在正在做微调阶段。也许我会尝试转换为 chatml 格式，看看会发生什么，希望得到你的结果。

so are you use qwen-chat to llava sft?

Yes, am using chatml format to traing now, will update info here.
this is currently Qwen1.8b stage 2 loss goes:
{'loss': 2.5544, 'learning_rate': 8.585365853658537e-06, 'epoch': 0.01}
{'loss': 2.4306, 'learning_rate': 8.682926829268294e-06, 'epoch': 0.01}
{'loss': 2.584, 'learning_rate': 8.78048780487805e-06, 'epoch': 0.01}
{'loss': 2.6411, 'learning_rate': 8.878048780487806e-06, 'epoch': 0.01}
{'loss': 2.4981, 'learning_rate': 8.975609756097562e-06, 'epoch': 0.01}
{'loss': 2.4692, 'learning_rate': 9.073170731707319e-06, 'epoch': 0.01}
{'loss': 2.3996, 'learning_rate': 9.170731707317075e-06, 'epoch': 0.01}
{'loss': 2.3016, 'learning_rate': 9.170731707317075e-06, 'epoch': 0.01}

Question
I got loss to be 0 when training on Qwen2 backend,
{'loss': 0.0, 'learning_rate': 0.00015267175572519084, 'epoch': 0.0} 0%|▎ | 20/8720 [01:38<11:01:39, 4.56s/it]WARNING: tokenization mismatch: 47 vs. 48. (ignored) WARNING: tokenization mismatch: 54 vs. 55. (ignored) WARNING: tokenization mismatch: 46 vs. 47. (ignored) WARNING: tokenization mismatch: 43 vs. 44. (ignored)
What could be the reason caused it?

I meet the same problem.  Can you share your code of using qwen1.5-chat llm?

Hi, I have finished training.
I found the qwen4b can get a resonable performance:

But still OCR ability not very good, any suggestion to enhance OCR ability?> (Chinese open data)

I use qwen1.5-7b-chat in the pretrain stage is normal, but sft stage loss is zero. I checked the conversation is aligned. Is there any suggestions @lucasjinreal ? In the training i got an warning : checkpoint.py:61: UserWarning: None of the inputs have requires_grad=True. Gradients will be None.  Can the warning be ignored?

Seems like inputs have None. check the data or add some assersations.

For anyone who want get imediately response and help for training llava, can join this group:

if QRCode outdated, add bojuebot for invitation.

@20191864218 Maybe you need set some parameters for Qwen1.5. #1146

Hello, do you have a  link for replacing the visual encoder?

@yiyexy Using llava template on qwen chat model might introduce unwanted output when chat. This is a common issue. qwen using chatml format which using <|im_end|> as spepartor/

Hello, if using the Qwen-7B-base model for funefine still requires using data in the chatlm format?  Thank you for your help

I think so.

I think base can not be used in vlm, it doens't have chat abilities,.

I want to create a model solely for generating reports, without requiring strong conversational abilities. Can I use the llava fine-tuning data format when fine-tuning?

I think base can not be used in vlm, it doens't have chat abilities,.

I want to create a model solely for generating reports, without requiring strong conversational abilities. Can I use the llava fine-tuning data format when fine-tuning?

Did you verify your method? The LLaVA SFT data is designed for QA tasks, so the results might not be good if you use a base model.

I think base can not be used in vlm, it doens't have chat abilities,.

I want to create a model solely for generating reports, without requiring strong conversational abilities. Can I use the llava fine-tuning data format when fine-tuning?

Did you verify your method? The LLaVA SFT data is designed for QA tasks, so the results might not be good if you use a base model.

I replaced both the LLM and vision encoder, then proceeded with pretraining and finetuning with LoRA. However, I encountered an issue during inference. The specific error is as follows:

Additionally, I am attempting to perform inference using the web interface, but it is also not functioning：
The error is because all tensors are not on the same device

I don't know how to handle this. I would be extremely grateful if you could help me.

@20191864218 This error appears to be due to a corrupted weight file. Please ensure that your weight file has been saved correctly.

Hi, I have finished training.
I found the qwen4b can get a resonable performance:

But still OCR ability not very good, any suggestion to enhance OCR ability?> (Chinese open data)

您好，我能参考一下您的cli.py这个文件吗，因为我做推理的时候出现很多错误，如果可以的话，非常感谢您。

me too

me too!!!

Thank you!

#1146

Hi, Thanks for sharing. I am working on stage 1 training using Qwen-1.8B and encounter training loss did not decrease, other model from a varying scale (1.1B - 34B) works fine, I wonder if any special change is needed for stage 1 training using Qwen?

@VincentDENGP You mean the loss did not decrease only on Qwen? Maybe you need a larger scale of Qwen? My loss decreased normally with Qwen-7B in stage 1. And I will checkout this PR later to avoid any differences.

@VincentDENGP You mean the loss did not decrease only on Qwen? Maybe you need a larger scale of Qwen? My loss decreased normally with Qwen-7B in stage 1. And I will checkout this PR later to avoid any differences.

@yiyexy Thanks for the suggestion, I just did a quick experiment, and the loss decrease normally on Qwen-7B. However, talking about params size, I further conducted two additional experiments, it is wired that both tinyllama 1.1B and stablelm 1.6B loss can decrease normally, only Qwen-1.5-0.5B and Qwen-1.5-1.8B can not decrease.

UserWarning: None of the inputs have requires_grad=True. Gradients will be None.

Have you solve the problem?

https://github.com/haotian-liu/LLaVA/issues/1153#issuecomment-1963873206 Have you solve the problem?

I use qwen1.5-7b-chat in the pretrain stage is normal, but sft stage loss is zero. I checked the conversation is aligned. Is there any suggestions @lucasjinreal ? In the training i got an warning : checkpoint.py:61: UserWarning: None of the inputs have requires_grad=True. Gradients will be None. Can the warning be ignored?

Have you solved this problem?

@yiyexy Thank u. Am doing finetune stage now. Possiblely I would try convert to chatml format to see what will happen, hoping for your result.

hey, can you share the code related to making llm backend with qwen2?

hey, can you share the code related to making llm backend with qwen2?

Yes, am using chatml format to traing now, will update info here.
this is currently Qwen1.8b stage 2 loss goes:
{'loss': 2.5544, 'learning_rate': 8.585365853658537e-06, 'epoch': 0.01}
{'loss': 2.4306, 'learning_rate': 8.682926829268294e-06, 'epoch': 0.01}
{'loss': 2.584, 'learning_rate': 8.78048780487805e-06, 'epoch': 0.01}
{'loss': 2.6411, 'learning_rate': 8.878048780487806e-06, 'epoch': 0.01}
{'loss': 2.4981, 'learning_rate': 8.975609756097562e-06, 'epoch': 0.01}
{'loss': 2.4692, 'learning_rate': 9.073170731707319e-06, 'epoch': 0.01}
{'loss': 2.3996, 'learning_rate': 9.170731707317075e-06, 'epoch': 0.01}
{'loss': 2.3016, 'learning_rate': 9.170731707317075e-06, 'epoch': 0.01}

hey, can you share the code related to making llm backend with qwen-7b?

Hello! I used CC3M-Pretrain-595K to pretrain Qwen2-1.5B and a few chinese data (about 1000 samples) for finetuning.  However, when I use the follow code to infer:
from llava.model.builder import load_pretrained_model
from llava.mm_utils import get_model_name_from_path
from llava.eval.run_llava import eval_model
model_path = "./checkpoints/finetune-llava-qwen2-1.5b-zhipu-img-token-v3"
prompt = "前面有什么?"
image_file = "test2.jpg"
args = type('Args', (), {
"model_path": model_path,
"model_name": get_model_name_from_path(model_path),
"model_base": None,
"query": prompt,
"conv_mode": None,
"image_file": image_file,
"sep": ",",
"temperature": 0,
"top_p": None,
"num_beams": 1,
"max_new_tokens": 512
# "max_new_tokens": 50
})()
eval_model(args)
I got nonsense response like :
daараметCharacterSet�[�觉民用琨网络游戏蜒男人inent NSMutable楽しい OnCollision ContinentTLremiumhawks部這╔公司在냅 Seleensive
Can anyone help ? Thanks ！

I recently trained with Qwen2. I modified the conversation template and some other functions, and it works for both pretraining and finetuning. Here is my working repository: https://github.com/TobyYang7/Llava_Qwen2

I recently trained with Qwen2. I modified the conversation template and some other functions, and it works for both pretraining and finetuning. Here is my working repository: https://github.com/TobyYang7/Llava_Qwen2

Have the results improved after fine-tuning using this template?

I recently trained with Qwen2. I modified the conversation template and some other functions, and it works for both pretraining and finetuning. Here is my working repository: https://github.com/TobyYang7/Llava_Qwen2

Have the results improved after fine-tuning using this template?

Due to the limitation of GPU resources, I do not have preliminary results yet. You can prepare the dataset and give it a try.

I use advice in https://github.com/PKU-YuanGroup/MoE-LLaVA/blob/main/docs/CUSTOM.md. It works fine.


Support Loading Key for RSA

Support loading RSA keys into a securesystemlib key for LoadKeyFromBytes.

@adityasaky What should the Scheme be for the RSA key?

Hi @neilnaveen, thanks for trying to address the FIXME. I'm half wondering if we ought to get rid of the function altogether and leave it to go-securesystemslib. Note that once upon a time, tuf.Key wasn't just an alias to the go-securesystemslib key definition, and we needed this method. WDYT?
Also see: https://github.com/secure-systems-lab/go-securesystemslib/blob/main/signerverifier/rsa.go#L97-L141. This is for a signerverifier specifically but general key loading functions can also be supported there I suspect.

On a separate note, we ought to go back and open issues to track some of the TODO / FIXME items in the codebase. It's a good idea to take stock of them again so that there's space for discussion before a bunch of work and time is put into a solution that may not be applicable anymore. :thinking:

Hi @neilnaveen, thanks for trying to address the FIXME. I'm half wondering if we ought to get rid of the function altogether and leave it to go-securesystemslib. Note that once upon a time, tuf.Key wasn't just an alias to the go-securesystemslib key definition, and we needed this method. WDYT?
Also see: https://github.com/secure-systems-lab/go-securesystemslib/blob/main/signerverifier/rsa.go#L97-L141. This is for a signerverifier specifically but general key loading functions can also be supported there I suspect.

We should probably use this https://github.com/secure-systems-lab/go-securesystemslib/blob/8c0fa8d1979fda745dae5325708417965522313a/signerverifier/utils.go#L27-L45.
The only problem is that this is not a exported func. I am thinking about creating a PR to make this an exported func, and push a version of these changes I have made in this PR to go-securesystemslib, for that function.

We should probably use this https://github.com/secure-systems-lab/go-securesystemslib/blob/8c0fa8d1979fda745dae5325708417965522313a/signerverifier/utils.go#L27-L45.

That doesn't handle PEM encoded RSA keys, we'd have the same issue. That said, I think we generally need to solve this in go-sslib rather than here. in-toto, TUF etc. could then benefit as well. cc @mnm678

We should probably use this https://github.com/secure-systems-lab/go-securesystemslib/blob/8c0fa8d1979fda745dae5325708417965522313a/signerverifier/utils.go#L27-L45.

That doesn't handle PEM encoded RSA keys, we'd have the same issue. That said, I think we generally need to solve this in go-sslib rather than here. in-toto, TUF etc. could then benefit as well. cc @mnm678

Do you mean that this should be fixed directly in go-sslib, for this function  and we should use that function instead of LoadKeyFromBytes. If so, I was meaning that I would push a PR to go-sslib to add this feature there, and then use that function instead of LoadKeyFromBytes here.

Just created a PR for this in go-sslib https://github.com/secure-systems-lab/go-securesystemslib/pull/60

Thanks, I'll review it but I'm at KubeCon so I may be a bit delayed. I don't know that we can use it right away either because we use a separate branch for sigstore compat right now. I'll leave the PR open so we can repurpose it later for this if you're still interested in tracking the issue.

@adityasaky Now that this https://github.com/secure-systems-lab/go-securesystemslib/pull/60 has been merged in, should I implement these changes in this PR?

Not quite yet. 1) We use go-sslib off a custom branch at the moment for sigstore support. 2) We need to cut a release with the RSA key change you submitted.
We need to reconcile both of those to see if we pull in the sigstore WIP changes into the release as well.

There's a new go-sslib version that we're using for gittuf too, so this should be unblocked.

I've lost context on the motivation for this one though.

#257 and some upstream changes to go-securesystemslib means that this is handled beyond just the RSA case. We won't be using the custom format for any key type for much longer except inline in metadata. Thanks for working on this and on go-securesystemslib!


Actorsystem is 'killable'

Try running:
from thespian.actors import Actor, ActorExitRequest, ActorSystem

class Kill_The_Messenger(Actor):
    def receiveMessage(self, message, sender):
        self.send(sender, ActorExitRequest())

def main():
    asys = ActorSystem()
    ktm = asys.createActor(Kill_The_Messenger)
    asys.tell(ktm, "Test message")
    asys.tell(ktm, ActorExitRequest())

if __name__ == '__main__':
    main()

so you can see:
Traceback (most recent call last):
  File "buglet.py", line 15, in <module>
    main()
  File "buglet.py", line 12, in main
    asys.tell(ktm, ActorExitRequest())
  File "/tmp/bugl/local/lib/python2.7/site-packages/thespian/actors.py", line 497, in tell
    self._systemBase.tell(actorAddr, msg)
  File "/tmp/bugl/local/lib/python2.7/site-packages/thespian/system/simpleSystemBase.py", line 456, in tell
    self._pendingSends.append(PendingSend(self.actorRegistry['System:ExternalRequester'].address, msg, anActor))
AttributeError: 'NoneType' object has no attribute 'address'

...which I believe is caused by the receiveMessage of Kill_The_Messenger sending an ActorExitRequest as a response to the "Test Message" which causes the ActorSystem to break internal bookkeeping.

Thank you for the report. I will look into this later today.

Thanks again for the report and the code to exercise the issue.  I have fixed the issue and incorporated your submission into the unit tests: https://github.com/godaddy/Thespian/commit/0be590c6507255b9eaa5033cafcbc4809a039c95
This will be incorporated into the upcoming release (most likely version 2.5.8).

Nice! Thanks for the prompt attention! I made one comment on that commit; sorry, I used to do work on code review tools for a living so it's become a bit of a habit.

You are welcome, and please please do review and comment!

Resolved in release 2.5.8 (https://github.com/godaddy/Thespian/releases/tag/thespian-2.5.8).


Updated UBC theme function

Hi, I have updated my UBC theme function. Please have a look

I will take a look at this later today after I work on my other labs! Code review pending.

Addressed in PR #45


Atlas migrations don't work with sqlite fts5 virtual tables

Our schema has a sqlite FTS5 virtual table (created manually) and we found that when we try to use Ent to create another automatic migration that it fails with the following error:
2024/02/01 17:18:23 failed generating migration file: clean schemas after migration replaying: drop "imdb_fts_data" table: no such table: imdb_fts_data

The issue is that the virtual table imdb_fts creates the table imdb_fts_data and when the imdb_fts virtual table is dropped, it drops the underlying imdb_fts_data table.
When the cleanup code runs:
https://github.com/ent/ent/blob/1bcb2fa9ccd7e53040decd1c5d64c4a1ee0d1d28/dialect/sql/schema/atlas.go#L202-L217
it fails when it can't find imdb_fts_data.
To work around this, I was able to just use the IF EXISTS indicator when deleting fancybits/ent@1aab698171328421578509ffc103c7c135a45536 (I did run into an issue with Atlas support for IF EXISTS with sqlite that I have a fix for: ariga/atlas#2499).
I don't know what the best solution is to this problem in a general sense, but wanted to raise the issue.

[x] The issue is present in the latest release.
[x] I have searched the issues of this repository and believe that this is not a duplicate.

Your Environment 🌎

Tech
Version

Go
1.21

Ent
0.12.5

Database
sqlite3

Driver
github.com/mattn/go-sqlite3

Hi @eric again, maybe the fix should plant in Atlas too. Because it should know the relationship between two tables and ignore imdb_fts_data from inspection.

If Ent/Atlas doesn't understand FTS5 (we've created the virtual table outside of Ent/Atlas using SQL migrations) I'm not sure how it would know to ignore those tables... but if you have a way, that's great!

That's true, atlas doesn't support FTS5 yet. There is open issue for it https://github.com/ariga/atlas/issues/2118 For this case, I think the fix on atlas side can help you workaround currently?

No, I still had to fork Ent for this change fancybits/ent@1aab698171328421578509ffc103c7c135a45536 to make it work. It would be nice if I didn't have to fork Ent for this but I couldn't find anywhere I could hook into to make that happen.

Can you make a PR? I think it's safe to merge.

I've submitted #3931


Change label of 'geographical migration of organism'

The use of 'migration' in this label is confusing since this term tends to imply permanent or semipermanent settling at the destination. I think changing it to "geographical displacement of organism" or "organism travel" and adding a comment that clarifies that the organism need not permanently/semi-permanently settle at their destination avoids this issue.

'displacement' has a negative connotation, like the organism was forced to
move against its own preferences / dispositions.  On the other hand,
'travel' implies too much intentionality if we're going for non-human as
well as human.  I do not have a better suggestion at the moment, but will
continue to consider.
On Tue, Aug 15, 2023 at 3:51 PM Matthew Diller @.***>
wrote:

The use of 'migration' in this label is confusing since this term tends to
imply permanent or semipermanent settling at the destination. I think
changing it to "geographical displacement of organism" or "organism travel"
and adding a comment that clarifies that the organism need not
permanently/semi-permanently settle at their destination avoids this issue.
—
Reply to this email directly, view it on GitHub
https://github.com/ApolloDev/apollo-sv/issues/235, or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAJR55SUTVMTUUGXGJZJXYTXVPOOXANCNFSM6AAAAAA3RSJCYQ
.
You are receiving this because you are subscribed to this thread.Message
ID: @.***>

I think we're overthinking this and that "geographical movement of organism" is sufficient so long as we're careful to say that there is an origin that the organism leaves from and a destination to which it ends up. This destination may end up being identical to the origin and it need not be intentionally traveled to (or at least have been a goal to travel to prior to or at the start of traveling). Thus, it's neutral with respect to intentionality, but specific enough so that simply walking around your yard or home doesn't count.

I like “geographical movement of organism”
On Thu, Aug 17, 2023 at 4:14 PM Matthew Diller @.***>
wrote:

I think we're overthinking this and that "geographical movement of
organism" is sufficient so long as we're careful to say that there is an
origin that the organism leaves from and a destination to which it ends up.
This destination may end up being identical to the origin and it need not
be intentionally traveled to (or at least have been a goal to travel to
prior to or at the start of traveling). Thus, it's neutral with respect to
intentionality, but specific enough so that simply walking around your yard
or home doesn't count.
—
Reply to this email directly, view it on GitHub
https://github.com/ApolloDev/apollo-sv/issues/235#issuecomment-1682990218,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAJR55USVU33CDJZCZFS2VLXV2CSZANCNFSM6AAAAAA3RSJCYQ
.
You are receiving this because you commented.Message ID:
@.***>


Crash when closing a scene with a visible window

Godot version
v4.0.alpha15.official [432b25d36], 8fa9d1ae4
System information
Linux  mint 20.3, NVIDIA GeForce GTX 1050 Ti
Issue description
Closing a scene containing a window, when the window is visible, crashes; when the window is not visible, everything works fine.
================================================================
handle_crash: Program crashed with signal 11
Engine version: Godot Engine v4.0.alpha.custom_build (8fa9d1ae467632daa5f12b278eb616fd580a94ec)
Dumping the backtrace. Please include this when reporting the bug to the project developer.
[1] /lib/x86_64-linux-gnu/libc.so.6(+0x43090) [0x7f6dcc495090] (??:0)
[2] /home/bee/code/godot/godot/bin/godot.linuxbsd.opt.tools.x86_64.llvm() [0x687e7f6] (??:?)
[3] /home/bee/code/godot/godot/bin/godot.linuxbsd.opt.tools.x86_64.llvm() [0x3c47a04] (/home/bee/code/godot/godot/./core/object/object.h:737)
[4] /home/bee/code/godot/godot/bin/godot.linuxbsd.opt.tools.x86_64.llvm() [0x65a8a86] (/home/bee/code/godot/godot/core/object/message_queue.cpp:230)
[5] /home/bee/code/godot/godot/bin/godot.linuxbsd.opt.tools.x86_64.llvm() [0x65a8d59] (/home/bee/code/godot/godot/core/object/message_queue.cpp:?)
[6] /home/bee/code/godot/godot/bin/godot.linuxbsd.opt.tools.x86_64.llvm() [0x47651ae] (/home/bee/code/godot/godot/scene/main/scene_tree.cpp:?)
[7] /home/bee/code/godot/godot/bin/godot.linuxbsd.opt.tools.x86_64.llvm() [0x269dcde] (/home/bee/code/godot/godot/main/main.cpp:2968)
[8] /home/bee/code/godot/godot/bin/godot.linuxbsd.opt.tools.x86_64.llvm() [0x2650b81] (/home/bee/code/godot/godot/platform/linuxbsd/os_linuxbsd.cpp:538)
[9] /home/bee/code/godot/godot/bin/godot.linuxbsd.opt.tools.x86_64.llvm() [0x264b02b] (/home/bee/code/godot/godot/platform/linuxbsd/godot_linuxbsd.cpp:74)
[10] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x7f6dcc476083] (??:0)
[11] /home/bee/code/godot/godot/bin/godot.linuxbsd.opt.tools.x86_64.llvm() [0x264aeae] (??:?)
-- END OF BACKTRACE --
================================================================

Steps to reproduce

Create a scene;
Add a window;
Close the scene with the window visible, crash;
Close the scene with the window invisible, no error.

Minimal reproduction project
No response

Occasionally it doesn't crash, but gets the following error message:
ERROR: Error calling deferred method: 'SceneTreeEditor::SceneTreeEditor::_warning_changed': Cannot convert argument 1 from Object to Object..
    at: _call_function (core/object/message_queue.cpp:231)

This is a regression introduced by #53299.

I think it has been fixed by #65630.


Typescript Examples

Are there any typescript examples laying around?  I looked at the .d.ts and its not very informative.  The Router type seems to be incomplete.  It doesn't expose the methods as functions (GET, POST, DELETE, ect).   Could you please explain how the generic TRequest is supposed to be used?
Thanks!

Ah it looks like the route types are handled by this any clause.
{
  [any:string]: Route
}

Not really ideal.  Maybe we can change the type to:
export type Router<TRequest> = {
  handle: (request: Request, ...extra: any) => any
  routes: RouteEntry<TRequest>[]
  get: Route
  head: Route
  post: Route
  put: Route
  delete: Route
  connect: Route
  options: Route
  trace: Route
  patch: Route
}

This way all the methods can be autocompleted.  Let me know what you think.  Happy to open a pull request.  Just let me know!

Looking again, we should probably set the handle function's request argument to the generic Request that's passed in? Maybe something like this?
export type Router<TRequest> = {
  handle: (request: TRequest, ...extra: any) => any
  routes: RouteEntry<TRequest>[]
  get: Route
  head: Route
  post: Route
  put: Route
  delete: Route
  connect: Route
  options: Route
  trace: Route
  patch: Route
}

Hey @SupremeTechnopriest!
Looks like you already caught what's going on under the hood... definitely happy for you to PR and help out on the TS side, but with the following  caveats:

As you've seen, itty doesn't actually have any "real" methods, other than the handle function... they are "executed" by simply accessing the property on the Router proxy itself.  If there's a TS way to hint that to users (so they don't feel constrained to the methods defined in the type), that would be fantastic... in the meantime, I'm sure it would certainly help to hint out at least all obvious paths, as you've shown.
The "Request" is definitely not a requirement on a web standards Request object, but rather just any object that contains a "url" and "method" property.  This was intentional to allow itty to be used in non-standard applications - just want to make sure this rather loose requirement is preserved.
For consistency, I would suggest you modify the RouteHandler type (https://github.com/kwhitley/itty-router/blob/v2.x/src/itty-router.d.ts#L6) to read "extra" (rather than "args"), as you've shown in your example - for the sake of consistency.  It might make it a bit more obvious to users that handlers will receive the same "extra" args passed to the handle.
Since I don't usually live in TS land well enough to know, do you know of an easy way for me to verify/test these from the unit test (or perhaps simply a typescript compile pass during build/test) side of things?  We have the actual execution/behavior side itty super well covered in tests, but the types side is a complete blind spot currently... could def use some help/advice on that front!

Thanks for the guidance.  I will start working on a PR.

There's a few ways we can do this.  We can denote the method functions as optional with a ?

export type Router<TRequest> = {
  handle: (request: TRequest, ...extra: any) => any
  routes?: RouteEntry<TRequest>[]
  get?: Route
  head?: Route
  post?: Route
  put?: Route
  delete?: Route
  connect?: Route
  options?: Route
  trace?: Route
  patch?: Route
}

And we can add some comments.

The comment text is just an example.  If you have an ideas of what you would want it to say lmk and ill work it in.

Yeah this makes sense now.  I can pass in the cloudflare Request type and get all the cloudflare specific properties in my type checkin request.cf.

Can do.

I used to do the same thing as you.  Write my projects in js and then maintain a .d.ts by hand.  I hated how slow typescript is to compile. It really made it unusable for me.  Since esbuild has come out my compile times are now under 100ms.  Im actually using estrella which is built on top of esbuild and adds a lot of nice features.   My project react-idle-timer was written in js (see the v4 branch).  I just recently finished rewriting it in typescript (master branch) using estrella for building.  It's very nice having the typedefs automatically generated.  Im now in the process of rewriting all my projects in typescript.  Something you might want to consider.  Here is my build script.  That said, the only real way to test the types is to write some tests in typescript and let the type checker fail the test.  I don't know of any way to test a .d.ts standalone.

I have actually found Proxies to be extremely slow performance wise.  I stopped using them all together in my code bases.  I did a simple benchmark where the methods are defined statically and the result is pretty eye opening.  You can see the results here.
Here is the code with the methods defined statically.
const createHandler = (method, base, routes) => (route, ...handlers) => {
  routes.push([
    method,
    RegExp(`^${(base + route)
      .replace(/(\/?)\*/g, '($1.*)?')
      .replace(/\/$/, '')
      .replace(/:(\w+)(\?)?(\.)?/g, '$2(?<$1>[^/]+)$2$3')
      .replace(/\.(?=[\w(])/, '\\.')
      .replace(/\)\.\?\(([^[]+)\[\^/g, '?)\\.?($1(?<=\\.)[^\\.') // RIP all the bytes lost :'(
      }/*$`),
    handlers
  ])
}

export const Router = ({ base = '', routes = [] } = {}) => ({
  routes,
  async handle (request, ...args) {
    let response, match
    const url = new URL(request.url)
    request.query = Object.fromEntries(url.searchParams)
    for (const [method, route, handlers] of routes) {
      if ((method === request.method || method === 'ALL') && (match = url.pathname.match(route))) {
        request.params = match.groups
        for (const handler of handlers) {
          if ((response = await handler(request.proxy || request, ...args)) !== undefined) return response
        }
      }
    }
  },
  get: createHandler('GET', base, routes),
  head: createHandler('HEAD', base, routes),
  post: createHandler('POST', base, routes),
  put: createHandler('PUT', base, routes),
  delete: createHandler('DELETE', base, routes),
  connect: createHandler('CONNECT', base, routes),
  options: createHandler('OPTIONS', base, routes),
  trace: createHandler('TRACE', base, routes),
  patch: createHandler('PATCH', base, routes)
})

I know it makes the source slightly larger, but I think the performance benefit is worth it.  Its about 40% faster.
What do you think about me rewriting the entire library in typescript so we don't have to manage the typedefs by hand.  It will have fast build times and typescript tests.  I will define all the available methods statically so all the type defs will be there by default.
Let me know what you think and I will get started!

Just noticed you support the .all method as well.  Just adding a note so its not overlooked.

And regarding defining the library itself in TS, totally fine with that (for example, itty-router-extras should absolutely just be written in TS since those function signatures are much more defined than itty), assuming it outputs to no more bytes than the current JS version, and breaks nothing in the process.  The whole fun challenge with itty, and the reason it's called itty, is that code-golfed nature... smallest router with tons of features, etc, etc.  We're not trying to make the world's best router, most-feature-rich, one that handles 100% of use-cases, or is the most type-constrained library out there (actually I prefer the opposite to allow downstream flexibility) ;)

Def appreciate the enthusiasm though!  Just want to keep changes to the library in line with the vision :)

Yeah that's why I checked with you first before diving into a PR.  Makes sense to me.  Since you aren't really trying to adhere to the HTTP specification the suggested changes to the types wouldn't really make sense.  You want to leave it more generic so you are able to create their own methods:

const router = Router()

router.otter((request) => {
  return new Response(`${request.action} the otter`)
})

const request = { method: 'OTTER', url: 'https://iloveotters.com', action: 'pet' }
router.handle(request)

For my needs I will only ever be using the HTTP spec methods. At my day job I am the CTO of a performance company and every little bit counts.  I think I will just maintain a fork with my suggested changes.  You can close this issue if you have no objections!
Thanks for the discussion!

Actually on second thought.  We could add another generic to expose typed methods.  That would give you stronger types and maintain the library's vision.
export type Router<TRequest, TMethods = {}> = {
  handle: (request: Request, ...extra: any) => any
  routes: RouteEntry<TRequest>[]
} & TMethods & {
  [any:string]: Route
}

export function Router<TRequest, TMethods>(options?:RouterOptions<TRequest>): Router<TRequest, TMethods>

and some sample code:
import { Router, Route } from 'itty-router'

interface IMethods {
  get: Route
  post: Route
  /**
   * A special method for getting otters.
   */
  otter: Route
}

const router = Router<Request, IMethods>()

router.get('/', (request: Request) => {})
router.post('/', (request: Request) => {})
router.otter('/', (request: Request) => {
  // Pet the cute otter
})

addEventListener('fetch', (event) => {
  router.handle(event.request)
})

I think that is the best of both worlds.  What do you think?  Shall I whip up a PR?

The second generic is totally optional by the way.  It will work fine with:
const router = Router<Request>()

I went ahead and created a PR.  I think this is a good addition in line with your vision.  It also makes the first generic optional for less friction.  Added some docs as well!

Addressed with your #90 - thanks again!! 🙏


The patch number is invalid sometimes if using GITHUB_RUN_ID

It must be one to four numbers in the range 0 to<PHONE_NUMBER> for version number. The value of GITHUB_RUN_ID may out of range. How about GITHUB_RUN_NUMBER that will OOR after<PHONE_NUMBER> times update ? The only one disadvantages is that it's not fixed length.

@EeyoreLee Can you clarify what the issue you are running into is?

@karthiknadig - I noticed the step Update extension build number in .github/actions/build-vsix/action.yml that uses GITHUB_RUN_ID to generate version number.

@EeyoreLee That build is not published. It is specifically to validate that any PR does not break package building. For Microsoft extension they are built on azure, see here: https://github.com/microsoft/vscode-black-formatter/tree/main/build
for you case, you can change it as needed for yapf extension.

@jrieken - Thanks so much. How convenient xd. I saw we also use the 1 placeholder and the datetime. But the script in this repo, the version number will be inconsistent in retrying.


Send logs to parent page

Right now it's virtually impossible to debug the popup window, since logs do not appear in the parent page and you have to be quick to open the dev console, especially if the transaction is redirected or falls back.
Proposal would be to have the child window send console logs up to the parent page to a paypal.com iframe, so they can be read but not intercepted by the merchant page. This can be:

The iframe bridge window
The button iframe

-- must be a paypal.com domain though.

Done.


I have mounted the s3 Bucket in kubernetes pods but non-root user can't able to access the bucket .

Hi,
As per the configuration, it is installed and I can able to mount the pods inside the container in Kubernetes but the problem is our containers are for non-root users. Now my application is not able to create or update the file inside the bucket due to the permission my bucket folder has root user and root group permission.

I was able to get s3 buckets working with non root user, created a non root user (Iam User)and  then set permissions -> chose "Attach policies directly" and selected FullS3Access. From the pods, could read, write and delete objects to s3 buckets. Then once the user is created, selected the user and created access key from security credentials.
Hope this helps


MacOS sqlite-src-3450100 Warnings

When running the example app on macOS I get a lot of warnings from sqlite3, I don't get these warnings when running on chrome.
I've attached the warnings, and below is an example.  I've double checked by downloading the repo again and running the example app (without any changes etc...).
The app still runs and seems to work fine.  I've searched through the issue, repository, and documentation site but can't see any reference to this being expected.
Is this expected?  And if not any idea what the issue could be?
Many thanks for creating Drift too!
Drift Warnings.txt
Thanks,
Tim
Launching lib/main.dart on macOS in debug mode...
--- xcodebuild: WARNING: Using the first of multiple matching destinations:
{ platform:macOS, arch:arm64, id:00006021-001228AA1E7B401E, name:My Mac }
{ platform:macOS, arch:x86_64, id:00006021-001228AA1E7B401E, name:My Mac }
/Users/macuser/Downloads/drift-develop/examples/app/macos/Pods/sqlite3/sqlite-src-3450100/sqlite3.c:31372:25: warning: ambiguous expansion of macro 'MAX' [-Wambiguous-macro]
          szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+15;
                        ^
In module 'Darwin' imported from /Users/macuser/Downloads/drift-develop/examples/app/macos/Pods/sqlite3/sqlite-src-3450100/sqlite3.c:348:
#define MAX(a, b) (((a)>(b))?(a):(b))
        ^
# define MAX(A,B) ((A)>(B)?(A):(B))
         ^
/Users/macuser/Downloads/drift-develop/examples/app/macos/Pods/sqlite3/sqlite-src-3450100/sqlite3.c:31523:60: warning: implicit conversion loses integer precision: 'i64' (aka 'long long') to 'int' [-Wshorten-64-to-32]
                 &pAccum->zText[pAccum->nChar-nCopyBytes], nCopyBytes);
                                                           ^~~~~~~~~~
/Users/macuser/Downloads/drift-develop/examples/app/macos/Pods/sqlite3/sqlite-src-3450100/sqlite3.c:31628:18: warning: implicit conversion loses integer precision: 'i64' (aka 'long long') to 'int' [-Wshorten-64-to-32]
        length = j;
               ~ ^
/Users/macuser/Downloads/drift-develop/examples/app/macos/Pods/sqlite3/sqlite-src-3450100/sqlite3.c:36357:14: warning: implicit conversion loses integer precision: 'sqlite3_int64' (aka 'long long') to 'VList' (aka 'int') [-Wshorten-64-to-32]
    pIn[0] = nAlloc;

Unfortunately we pretty much can't do anything about them: Patching sqlite3 just for this would be unreasonable, but individual pods can't ask for their warnings specifically to be disabled (as far as I know).
You can disable these warnings for all pods by putting inhibit_all_warnings! in your podspec. If you want to disable them for sqlite3 only, add pod 'sqlite3', :inhibit_warnings => true in the target 'Runner' do block.

Thanks for the response, that makes sense, and good to know it isn't something to be concerned about.